dabke 0.82.0 → 0.83.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 (83) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +6 -3
  3. package/dist/cpsat/rules/index.d.ts +3 -0
  4. package/dist/cpsat/rules/index.d.ts.map +1 -1
  5. package/dist/cpsat/rules/index.js +3 -0
  6. package/dist/cpsat/rules/index.js.map +1 -1
  7. package/dist/cpsat/rules/max-days-week.d.ts +44 -0
  8. package/dist/cpsat/rules/max-days-week.d.ts.map +1 -0
  9. package/dist/cpsat/rules/max-days-week.js +95 -0
  10. package/dist/cpsat/rules/max-days-week.js.map +1 -0
  11. package/dist/cpsat/rules/min-days-week.d.ts +34 -0
  12. package/dist/cpsat/rules/min-days-week.d.ts.map +1 -0
  13. package/dist/cpsat/rules/min-days-week.js +84 -0
  14. package/dist/cpsat/rules/min-days-week.js.map +1 -0
  15. package/dist/cpsat/rules/must-assign.d.ts +49 -0
  16. package/dist/cpsat/rules/must-assign.d.ts.map +1 -0
  17. package/dist/cpsat/rules/must-assign.js +86 -0
  18. package/dist/cpsat/rules/must-assign.js.map +1 -0
  19. package/dist/cpsat/rules/registry.d.ts +4 -1
  20. package/dist/cpsat/rules/registry.d.ts.map +1 -1
  21. package/dist/cpsat/rules/registry.js +4 -1
  22. package/dist/cpsat/rules/registry.js.map +1 -1
  23. package/dist/cpsat/rules/rules.types.d.ts +3 -0
  24. package/dist/cpsat/rules/rules.types.d.ts.map +1 -1
  25. package/dist/cpsat/rules/scope.types.d.ts +1 -1
  26. package/dist/index.d.ts +5 -3
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +4 -2
  29. package/dist/index.js.map +1 -1
  30. package/dist/schedule/cost.d.ts +204 -0
  31. package/dist/schedule/cost.d.ts.map +1 -0
  32. package/dist/schedule/cost.js +187 -0
  33. package/dist/schedule/cost.js.map +1 -0
  34. package/dist/schedule/coverage.d.ts +85 -0
  35. package/dist/schedule/coverage.d.ts.map +1 -0
  36. package/dist/schedule/coverage.js +33 -0
  37. package/dist/schedule/coverage.js.map +1 -0
  38. package/dist/schedule/definition.d.ts +227 -0
  39. package/dist/schedule/definition.d.ts.map +1 -0
  40. package/dist/{schedule.js → schedule/definition.js} +9 -673
  41. package/dist/schedule/definition.js.map +1 -0
  42. package/dist/schedule/index.d.ts +67 -0
  43. package/dist/schedule/index.d.ts.map +1 -0
  44. package/dist/schedule/index.js +69 -0
  45. package/dist/schedule/index.js.map +1 -0
  46. package/dist/schedule/rules.d.ts +353 -0
  47. package/dist/schedule/rules.d.ts.map +1 -0
  48. package/dist/schedule/rules.js +352 -0
  49. package/dist/schedule/rules.js.map +1 -0
  50. package/dist/schedule/shift-patterns.d.ts +34 -0
  51. package/dist/schedule/shift-patterns.d.ts.map +1 -0
  52. package/dist/schedule/shift-patterns.js +41 -0
  53. package/dist/schedule/shift-patterns.js.map +1 -0
  54. package/dist/schedule/time-periods.d.ts +69 -0
  55. package/dist/schedule/time-periods.d.ts.map +1 -0
  56. package/dist/schedule/time-periods.js +91 -0
  57. package/dist/schedule/time-periods.js.map +1 -0
  58. package/package.json +4 -9
  59. package/src/cpsat/rules/index.ts +3 -0
  60. package/src/cpsat/rules/max-days-week.ts +143 -0
  61. package/src/cpsat/rules/min-days-week.ts +120 -0
  62. package/src/cpsat/rules/must-assign.ts +108 -0
  63. package/src/cpsat/rules/registry.ts +6 -0
  64. package/src/cpsat/rules/rules.types.ts +3 -0
  65. package/src/cpsat/rules/scope.types.ts +1 -1
  66. package/src/index.ts +8 -3
  67. package/src/schedule/cost.ts +242 -0
  68. package/src/schedule/coverage.ts +135 -0
  69. package/src/schedule/definition.ts +958 -0
  70. package/src/schedule/index.ts +112 -0
  71. package/src/schedule/rules.ts +529 -0
  72. package/src/schedule/shift-patterns.ts +46 -0
  73. package/src/schedule/time-periods.ts +110 -0
  74. package/dist/llms.d.ts +0 -2
  75. package/dist/llms.d.ts.map +0 -1
  76. package/dist/llms.js +0 -3
  77. package/dist/llms.js.map +0 -1
  78. package/dist/schedule.d.ts +0 -917
  79. package/dist/schedule.d.ts.map +0 -1
  80. package/dist/schedule.js.map +0 -1
  81. package/llms.txt +0 -758
  82. package/src/llms.ts +0 -3
  83. package/src/schedule.ts +0 -1960
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Cost optimization rules: minimize labor cost with modifiers.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import type { DayOfWeek, TimeOfDay } from "../types.js";
8
+ import type { OvertimeTier } from "../cpsat/rules/overtime-tiered-multiplier.js";
9
+ import type { RecurringPeriod } from "../cpsat/rules/scope.types.js";
10
+ import type { RuleEntry } from "./rules.js";
11
+ import { defineRule } from "./rules.js";
12
+
13
+ // ============================================================================
14
+ // Cost Rule Options
15
+ // ============================================================================
16
+
17
+ /**
18
+ * Options for cost rules.
19
+ *
20
+ * Cost rules are objective terms, not constraints. The `priority` field from
21
+ * {@link RuleOptions} does not apply.
22
+ *
23
+ * @category Cost Optimization
24
+ */
25
+ export interface CostRuleOptions {
26
+ /** Who this rule applies to (role name, skill name, or member ID). */
27
+ appliesTo?: string | string[];
28
+ /** Restrict to specific days of the week. */
29
+ dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]];
30
+ /** Restrict to a date range. */
31
+ dateRange?: { start: string; end: string };
32
+ /** Restrict to specific dates (YYYY-MM-DD). */
33
+ dates?: string[];
34
+ /** Restrict to recurring calendar periods. */
35
+ recurringPeriods?: [RecurringPeriod, ...RecurringPeriod[]];
36
+ }
37
+
38
+ // ============================================================================
39
+ // Cost Rules
40
+ // ============================================================================
41
+
42
+ function makeCostRule(rule: string, fields: Record<string, unknown>): RuleEntry {
43
+ return defineRule(rule, fields);
44
+ }
45
+
46
+ /**
47
+ * Tells the solver to minimize total labor cost.
48
+ *
49
+ * @remarks
50
+ * Without this rule, cost modifiers only affect post-solve calculation.
51
+ * When present, the solver actively prefers cheaper assignments.
52
+ *
53
+ * For hourly members, penalizes each assignment proportionally to cost.
54
+ * For salaried members, adds a fixed weekly salary cost when they have
55
+ * any assignment that week (zero marginal cost up to contracted hours).
56
+ *
57
+ * Cost modifiers adjust the calculation:
58
+ * - `dayMultiplier(factor, opts?)` - multiply base rate on specific days
59
+ * - `daySurcharge(amount, opts?)` - flat extra per hour on specific days
60
+ * - `timeSurcharge(amount, window, opts?)` - flat extra per hour during a time window
61
+ * - `overtimeMultiplier({ after, factor }, opts?)` - weekly overtime multiplier
62
+ * - `overtimeSurcharge({ after, amount }, opts?)` - weekly overtime surcharge
63
+ * - `dailyOvertimeMultiplier({ after, factor }, opts?)` - daily overtime multiplier
64
+ * - `dailyOvertimeSurcharge({ after, amount }, opts?)` - daily overtime surcharge
65
+ * - `tieredOvertimeMultiplier(tiers, opts?)` - multiple overtime thresholds
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * minimizeCost()
70
+ * ```
71
+ *
72
+ * @category Cost Optimization
73
+ */
74
+ export function minimizeCost(opts?: CostRuleOptions): RuleEntry {
75
+ return makeCostRule("minimize-cost", { ...opts });
76
+ }
77
+
78
+ /**
79
+ * Multiplies the base rate for assignments on specified days.
80
+ *
81
+ * @remarks
82
+ * The base cost (1x) is already counted by {@link minimizeCost};
83
+ * this rule adds only the extra portion above 1x.
84
+ *
85
+ * @category Cost Optimization
86
+ *
87
+ * @example Weekend multiplier
88
+ * ```typescript
89
+ * dayMultiplier(1.5, { dayOfWeek: weekend })
90
+ * ```
91
+ */
92
+ export function dayMultiplier(factor: number, opts?: CostRuleOptions): RuleEntry {
93
+ return makeCostRule("day-cost-multiplier", { factor, ...opts });
94
+ }
95
+
96
+ /**
97
+ * Adds a flat extra amount per hour for assignments on specified days.
98
+ *
99
+ * @remarks
100
+ * The surcharge is independent of the member's base rate.
101
+ *
102
+ * @category Cost Optimization
103
+ *
104
+ * @example Weekend surcharge
105
+ * ```typescript
106
+ * daySurcharge(500, { dayOfWeek: weekend })
107
+ * ```
108
+ */
109
+ export function daySurcharge(amountPerHour: number, opts?: CostRuleOptions): RuleEntry {
110
+ return makeCostRule("day-cost-surcharge", { amountPerHour, ...opts });
111
+ }
112
+
113
+ /**
114
+ * Adds a flat surcharge per hour for the portion of a shift that overlaps a time-of-day window.
115
+ *
116
+ * @remarks
117
+ * The window supports overnight spans (e.g., 22:00-06:00). The surcharge
118
+ * is independent of the member's base rate.
119
+ *
120
+ * @param amountPerHour - Flat surcharge per hour in smallest currency unit
121
+ * @param window - Time-of-day window
122
+ * @param opts - Entity and time scoping
123
+ *
124
+ * @category Cost Optimization
125
+ *
126
+ * @example Night differential
127
+ * ```typescript
128
+ * timeSurcharge(200, { from: t(22), until: t(6) })
129
+ * ```
130
+ */
131
+ export function timeSurcharge(
132
+ amountPerHour: number,
133
+ window: { from: TimeOfDay; until: TimeOfDay },
134
+ opts?: CostRuleOptions,
135
+ ): RuleEntry {
136
+ return makeCostRule("time-cost-surcharge", { amountPerHour, window, ...opts });
137
+ }
138
+
139
+ /**
140
+ * Applies a multiplier to hours beyond a weekly threshold.
141
+ *
142
+ * @remarks
143
+ * Only the extra portion above 1x is added (the base cost is already
144
+ * counted by {@link minimizeCost}).
145
+ *
146
+ * @category Cost Optimization
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * overtimeMultiplier({ after: 40, factor: 1.5 })
151
+ * ```
152
+ */
153
+ export function overtimeMultiplier(
154
+ opts: { after: number; factor: number } & CostRuleOptions,
155
+ ): RuleEntry {
156
+ return makeCostRule("overtime-weekly-multiplier", { ...opts });
157
+ }
158
+
159
+ /**
160
+ * Adds a flat surcharge per hour beyond a weekly threshold.
161
+ *
162
+ * @remarks
163
+ * The surcharge is independent of the member's base rate.
164
+ *
165
+ * @category Cost Optimization
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * overtimeSurcharge({ after: 40, amount: 1000 })
170
+ * ```
171
+ */
172
+ export function overtimeSurcharge(
173
+ opts: { after: number; amount: number } & CostRuleOptions,
174
+ ): RuleEntry {
175
+ return makeCostRule("overtime-weekly-surcharge", { ...opts });
176
+ }
177
+
178
+ /**
179
+ * Applies a multiplier to hours beyond a daily threshold.
180
+ *
181
+ * @remarks
182
+ * Only the extra portion above 1x is added (the base cost is already
183
+ * counted by {@link minimizeCost}).
184
+ *
185
+ * @category Cost Optimization
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * dailyOvertimeMultiplier({ after: 8, factor: 1.5 })
190
+ * ```
191
+ */
192
+ export function dailyOvertimeMultiplier(
193
+ opts: { after: number; factor: number } & CostRuleOptions,
194
+ ): RuleEntry {
195
+ return makeCostRule("overtime-daily-multiplier", { ...opts });
196
+ }
197
+
198
+ /**
199
+ * Adds a flat surcharge per hour beyond a daily threshold.
200
+ *
201
+ * @remarks
202
+ * The surcharge is independent of the member's base rate.
203
+ *
204
+ * @category Cost Optimization
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * dailyOvertimeSurcharge({ after: 8, amount: 500 })
209
+ * ```
210
+ */
211
+ export function dailyOvertimeSurcharge(
212
+ opts: { after: number; amount: number } & CostRuleOptions,
213
+ ): RuleEntry {
214
+ return makeCostRule("overtime-daily-surcharge", { ...opts });
215
+ }
216
+
217
+ /**
218
+ * Applies multiple overtime thresholds with increasing multipliers.
219
+ *
220
+ * @remarks
221
+ * Each tier applies only to the hours between its threshold and the next.
222
+ * Tiers must be sorted by threshold ascending.
223
+ *
224
+ * @category Cost Optimization
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * // Hours 0-40: base rate
229
+ * // Hours 40-48: 1.5x
230
+ * // Hours 48+: 2.0x
231
+ * tieredOvertimeMultiplier([
232
+ * { after: 40, factor: 1.5 },
233
+ * { after: 48, factor: 2.0 },
234
+ * ])
235
+ * ```
236
+ */
237
+ export function tieredOvertimeMultiplier(
238
+ tiers: [OvertimeTier, ...OvertimeTier[]],
239
+ opts?: CostRuleOptions,
240
+ ): RuleEntry {
241
+ return makeCostRule("overtime-tiered-multiplier", { tiers, ...opts });
242
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Coverage definitions: staffing requirements per semantic time period.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import type { DayOfWeek } from "../types.js";
8
+ import type { CoverageVariant } from "../cpsat/semantic-time.js";
9
+ import type { Priority } from "../cpsat/types.js";
10
+
11
+ export type { CoverageVariant } from "../cpsat/semantic-time.js";
12
+
13
+ /**
14
+ * Options for a {@link cover} call.
15
+ *
16
+ * @remarks
17
+ * Day/date scoping controls which days this coverage entry applies to.
18
+ * An entry without `dayOfWeek` or `dates` applies every day in the
19
+ * scheduling period.
20
+ *
21
+ * @category Coverage
22
+ */
23
+ export interface CoverageOptions {
24
+ /** Additional skill ID filter (AND logic with the target role). */
25
+ skillIds?: [string, ...string[]];
26
+ /** Restrict to specific days of the week. */
27
+ dayOfWeek?: readonly [DayOfWeek, ...DayOfWeek[]];
28
+ /** Restrict to specific dates (YYYY-MM-DD). */
29
+ dates?: string[];
30
+ /** Defaults to `"MANDATORY"`. */
31
+ priority?: Priority;
32
+ }
33
+
34
+ /**
35
+ * A coverage entry returned by {@link cover}.
36
+ *
37
+ * @remarks
38
+ * Carries the semantic time name and target type information for
39
+ * compile-time validation by {@link schedule}. This is an opaque
40
+ * token; pass it directly into the `coverage` array.
41
+ */
42
+ export interface CoverageEntry<T extends string = string, R extends string = string> {
43
+ /** @internal */ readonly _type: "coverage";
44
+ /** @internal */ readonly timeName: T;
45
+ /** @internal */ readonly target: R | R[];
46
+ /** @internal */ readonly count: number;
47
+ /** @internal */ readonly options: CoverageOptions;
48
+ /** @internal When present, this entry uses variant-based resolution. */
49
+ readonly variants?: readonly CoverageVariant[];
50
+ }
51
+
52
+ /**
53
+ * Defines a staffing requirement for a semantic time period.
54
+ *
55
+ * @remarks
56
+ * Entries for the same time and role **stack additively**.
57
+ * For weekday vs weekend staffing, use mutually exclusive `dayOfWeek`
58
+ * on both entries.
59
+ *
60
+ * @param timeName - Name of a declared semantic time
61
+ * @param target - Role name (string), array of role names (OR logic), or skill name
62
+ * @param count - Number of people needed
63
+ * @param opts - Options: `skillIds` (AND filter), `dayOfWeek`, `dates`, `priority`
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * coverage: [
68
+ * // 2 waiters during lunch
69
+ * cover("lunch", "waiter", 2),
70
+ *
71
+ * // 1 manager OR supervisor during dinner
72
+ * cover("dinner", ["manager", "supervisor"], 1),
73
+ *
74
+ * // 1 person with keyholder skill at opening
75
+ * cover("opening", "keyholder", 1),
76
+ *
77
+ * // 1 senior waiter (role + skill AND)
78
+ * cover("lunch", "waiter", 1, { skillIds: ["senior"] }),
79
+ *
80
+ * // Different counts by day (mutually exclusive dayOfWeek!)
81
+ * cover("lunch", "waiter", 2, { dayOfWeek: weekdays }),
82
+ * cover("lunch", "waiter", 3, { dayOfWeek: weekend }),
83
+ * ]
84
+ * ```
85
+ *
86
+ * @category Coverage
87
+ */
88
+ export function cover<T extends string, const R extends string>(
89
+ timeName: T,
90
+ target: R | [R, ...R[]],
91
+ count: number,
92
+ opts?: CoverageOptions,
93
+ ): CoverageEntry<T, R>;
94
+ export function cover<T extends string, const R extends string>(
95
+ timeName: T,
96
+ target: R | [R, ...R[]],
97
+ ...variants: [CoverageVariant, ...CoverageVariant[]]
98
+ ): CoverageEntry<T, R>;
99
+ export function cover<T extends string, R extends string>(
100
+ timeName: T,
101
+ target: R | [R, ...R[]],
102
+ countOrFirstVariant: number | CoverageVariant,
103
+ ...rest: unknown[]
104
+ ): CoverageEntry<T, R> {
105
+ if (typeof countOrFirstVariant === "number") {
106
+ // Simple form: cover(time, target, count, opts?)
107
+ return {
108
+ _type: "coverage",
109
+ timeName,
110
+ target,
111
+ count: countOrFirstVariant,
112
+ options: (rest[0] as CoverageOptions | undefined) ?? {},
113
+ };
114
+ }
115
+
116
+ // Variant form: cover(time, target, ...variants)
117
+ const variants = [countOrFirstVariant, ...(rest as CoverageVariant[])];
118
+
119
+ const defaults = variants.filter((v) => !v.dayOfWeek && !v.dates);
120
+ if (defaults.length > 1) {
121
+ throw new Error(
122
+ "cover() accepts at most one default variant (without dayOfWeek or dates). " +
123
+ `Found ${defaults.length} default variants.`,
124
+ );
125
+ }
126
+
127
+ return {
128
+ _type: "coverage",
129
+ timeName,
130
+ target,
131
+ count: 0,
132
+ options: {},
133
+ variants,
134
+ };
135
+ }