@higher.archi/boe 1.0.30 → 1.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/evaluation/decay.d.ts +13 -2
- package/dist/core/evaluation/decay.d.ts.map +1 -1
- package/dist/core/evaluation/decay.js +24 -0
- package/dist/core/evaluation/decay.js.map +1 -1
- package/dist/core/types/rule.d.ts +25 -1
- package/dist/core/types/rule.d.ts.map +1 -1
- package/dist/core/types/rule.js +28 -0
- package/dist/core/types/rule.js.map +1 -1
- package/dist/engines/bayesian/index.d.ts +1 -1
- package/dist/engines/bayesian/index.d.ts.map +1 -1
- package/dist/engines/bayesian/index.js +2 -1
- package/dist/engines/bayesian/index.js.map +1 -1
- package/dist/engines/bayesian/types.d.ts +10 -1
- package/dist/engines/bayesian/types.d.ts.map +1 -1
- package/dist/engines/bayesian/types.js +16 -1
- package/dist/engines/bayesian/types.js.map +1 -1
- package/dist/engines/constraint/types.d.ts +36 -5
- package/dist/engines/constraint/types.d.ts.map +1 -1
- package/dist/engines/constraint/types.js +44 -1
- package/dist/engines/constraint/types.js.map +1 -1
- package/dist/engines/decay/index.d.ts +1 -1
- package/dist/engines/decay/index.d.ts.map +1 -1
- package/dist/engines/decay/index.js +5 -1
- package/dist/engines/decay/index.js.map +1 -1
- package/dist/engines/decay/types.d.ts +26 -4
- package/dist/engines/decay/types.d.ts.map +1 -1
- package/dist/engines/decay/types.js +30 -1
- package/dist/engines/decay/types.js.map +1 -1
- package/dist/engines/defeasible/index.d.ts +1 -1
- package/dist/engines/defeasible/index.d.ts.map +1 -1
- package/dist/engines/defeasible/index.js +8 -1
- package/dist/engines/defeasible/index.js.map +1 -1
- package/dist/engines/defeasible/types.d.ts +40 -5
- package/dist/engines/defeasible/types.d.ts.map +1 -1
- package/dist/engines/defeasible/types.js +56 -1
- package/dist/engines/defeasible/types.js.map +1 -1
- package/dist/engines/ensemble/index.d.ts +1 -0
- package/dist/engines/ensemble/index.d.ts.map +1 -1
- package/dist/engines/ensemble/index.js +5 -1
- package/dist/engines/ensemble/index.js.map +1 -1
- package/dist/engines/ensemble/types.d.ts +17 -2
- package/dist/engines/ensemble/types.d.ts.map +1 -1
- package/dist/engines/ensemble/types.js +23 -0
- package/dist/engines/ensemble/types.js.map +1 -1
- package/dist/engines/expert/index.d.ts +1 -1
- package/dist/engines/expert/index.d.ts.map +1 -1
- package/dist/engines/expert/index.js +3 -1
- package/dist/engines/expert/index.js.map +1 -1
- package/dist/engines/expert/types.d.ts +11 -1
- package/dist/engines/expert/types.d.ts.map +1 -1
- package/dist/engines/expert/types.js +18 -1
- package/dist/engines/expert/types.js.map +1 -1
- package/dist/engines/fuzzy/fuzzy.types.d.ts +65 -8
- package/dist/engines/fuzzy/fuzzy.types.d.ts.map +1 -1
- package/dist/engines/fuzzy/fuzzy.types.js +89 -1
- package/dist/engines/fuzzy/fuzzy.types.js.map +1 -1
- package/dist/engines/loyalty/index.d.ts +1 -0
- package/dist/engines/loyalty/index.d.ts.map +1 -1
- package/dist/engines/loyalty/index.js +8 -1
- package/dist/engines/loyalty/index.js.map +1 -1
- package/dist/engines/loyalty/types.d.ts +36 -5
- package/dist/engines/loyalty/types.d.ts.map +1 -1
- package/dist/engines/loyalty/types.js +40 -0
- package/dist/engines/loyalty/types.js.map +1 -1
- package/dist/engines/menu-engineering/compiler.d.ts +11 -0
- package/dist/engines/menu-engineering/compiler.d.ts.map +1 -0
- package/dist/engines/menu-engineering/compiler.js +119 -0
- package/dist/engines/menu-engineering/compiler.js.map +1 -0
- package/dist/engines/menu-engineering/engine.d.ts +32 -0
- package/dist/engines/menu-engineering/engine.d.ts.map +1 -0
- package/dist/engines/menu-engineering/engine.js +40 -0
- package/dist/engines/menu-engineering/engine.js.map +1 -0
- package/dist/engines/menu-engineering/index.d.ts +9 -0
- package/dist/engines/menu-engineering/index.d.ts.map +1 -0
- package/dist/engines/menu-engineering/index.js +21 -0
- package/dist/engines/menu-engineering/index.js.map +1 -0
- package/dist/engines/menu-engineering/strategy.d.ts +18 -0
- package/dist/engines/menu-engineering/strategy.d.ts.map +1 -0
- package/dist/engines/menu-engineering/strategy.js +318 -0
- package/dist/engines/menu-engineering/strategy.js.map +1 -0
- package/dist/engines/menu-engineering/types.d.ts +187 -0
- package/dist/engines/menu-engineering/types.d.ts.map +1 -0
- package/dist/engines/menu-engineering/types.js +27 -0
- package/dist/engines/menu-engineering/types.js.map +1 -0
- package/dist/engines/monte-carlo/index.d.ts +1 -1
- package/dist/engines/monte-carlo/index.d.ts.map +1 -1
- package/dist/engines/monte-carlo/index.js +5 -1
- package/dist/engines/monte-carlo/index.js.map +1 -1
- package/dist/engines/monte-carlo/types.d.ts +16 -1
- package/dist/engines/monte-carlo/types.d.ts.map +1 -1
- package/dist/engines/monte-carlo/types.js +23 -1
- package/dist/engines/monte-carlo/types.js.map +1 -1
- package/dist/engines/negotiation/index.d.ts +1 -0
- package/dist/engines/negotiation/index.d.ts.map +1 -1
- package/dist/engines/negotiation/index.js +7 -1
- package/dist/engines/negotiation/index.js.map +1 -1
- package/dist/engines/negotiation/types.d.ts +23 -4
- package/dist/engines/negotiation/types.d.ts.map +1 -1
- package/dist/engines/negotiation/types.js +27 -0
- package/dist/engines/negotiation/types.js.map +1 -1
- package/dist/engines/prediction/index.d.ts +1 -1
- package/dist/engines/prediction/index.d.ts.map +1 -1
- package/dist/engines/prediction/index.js +6 -1
- package/dist/engines/prediction/index.js.map +1 -1
- package/dist/engines/prediction/types.d.ts +35 -5
- package/dist/engines/prediction/types.d.ts.map +1 -1
- package/dist/engines/prediction/types.js +39 -1
- package/dist/engines/prediction/types.js.map +1 -1
- package/dist/engines/pricing/index.d.ts +2 -2
- package/dist/engines/pricing/index.d.ts.map +1 -1
- package/dist/engines/pricing/index.js +3 -1
- package/dist/engines/pricing/index.js.map +1 -1
- package/dist/engines/pricing/types.d.ts +15 -1
- package/dist/engines/pricing/types.d.ts.map +1 -1
- package/dist/engines/pricing/types.js +16 -1
- package/dist/engines/pricing/types.js.map +1 -1
- package/dist/engines/ranking/index.d.ts +1 -1
- package/dist/engines/ranking/index.d.ts.map +1 -1
- package/dist/engines/ranking/index.js +6 -1
- package/dist/engines/ranking/index.js.map +1 -1
- package/dist/engines/ranking/types.d.ts +32 -5
- package/dist/engines/ranking/types.d.ts.map +1 -1
- package/dist/engines/ranking/types.js +36 -1
- package/dist/engines/ranking/types.js.map +1 -1
- package/dist/engines/recipe-costing/compiler.d.ts +11 -0
- package/dist/engines/recipe-costing/compiler.d.ts.map +1 -0
- package/dist/engines/recipe-costing/compiler.js +177 -0
- package/dist/engines/recipe-costing/compiler.js.map +1 -0
- package/dist/engines/recipe-costing/engine.d.ts +32 -0
- package/dist/engines/recipe-costing/engine.d.ts.map +1 -0
- package/dist/engines/recipe-costing/engine.js +40 -0
- package/dist/engines/recipe-costing/engine.js.map +1 -0
- package/dist/engines/recipe-costing/index.d.ts +9 -0
- package/dist/engines/recipe-costing/index.d.ts.map +1 -0
- package/dist/engines/recipe-costing/index.js +21 -0
- package/dist/engines/recipe-costing/index.js.map +1 -0
- package/dist/engines/recipe-costing/strategy.d.ts +20 -0
- package/dist/engines/recipe-costing/strategy.d.ts.map +1 -0
- package/dist/engines/recipe-costing/strategy.js +265 -0
- package/dist/engines/recipe-costing/strategy.js.map +1 -0
- package/dist/engines/recipe-costing/types.d.ts +213 -0
- package/dist/engines/recipe-costing/types.d.ts.map +1 -0
- package/dist/engines/recipe-costing/types.js +36 -0
- package/dist/engines/recipe-costing/types.js.map +1 -0
- package/dist/engines/scoring/index.d.ts +1 -1
- package/dist/engines/scoring/index.d.ts.map +1 -1
- package/dist/engines/scoring/index.js +3 -1
- package/dist/engines/scoring/index.js.map +1 -1
- package/dist/engines/scoring/types.d.ts +8 -1
- package/dist/engines/scoring/types.d.ts.map +1 -1
- package/dist/engines/scoring/types.js +18 -1
- package/dist/engines/scoring/types.js.map +1 -1
- package/dist/engines/sentiment/index.d.ts +1 -1
- package/dist/engines/sentiment/index.d.ts.map +1 -1
- package/dist/engines/sentiment/index.js +3 -1
- package/dist/engines/sentiment/index.js.map +1 -1
- package/dist/engines/sentiment/types.d.ts +13 -2
- package/dist/engines/sentiment/types.d.ts.map +1 -1
- package/dist/engines/sentiment/types.js +17 -1
- package/dist/engines/sentiment/types.js.map +1 -1
- package/dist/engines/state-machine/index.d.ts +1 -1
- package/dist/engines/state-machine/index.d.ts.map +1 -1
- package/dist/engines/state-machine/index.js +5 -1
- package/dist/engines/state-machine/index.js.map +1 -1
- package/dist/engines/state-machine/types.d.ts +7 -0
- package/dist/engines/state-machine/types.d.ts.map +1 -1
- package/dist/engines/state-machine/types.js +14 -0
- package/dist/engines/state-machine/types.js.map +1 -1
- package/dist/engines/utility/index.d.ts +2 -2
- package/dist/engines/utility/index.d.ts.map +1 -1
- package/dist/engines/utility/index.js +4 -1
- package/dist/engines/utility/index.js.map +1 -1
- package/dist/engines/utility/types.d.ts +21 -3
- package/dist/engines/utility/types.d.ts.map +1 -1
- package/dist/engines/utility/types.js +37 -1
- package/dist/engines/utility/types.js.map +1 -1
- package/dist/index.d.ts +28 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +73 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/evaluation/decay.ts +13 -2
- package/src/core/types/rule.ts +25 -1
- package/src/engines/bayesian/index.ts +1 -0
- package/src/engines/bayesian/types.ts +10 -8
- package/src/engines/constraint/types.ts +40 -11
- package/src/engines/decay/index.ts +4 -0
- package/src/engines/decay/types.ts +26 -4
- package/src/engines/defeasible/index.ts +7 -0
- package/src/engines/defeasible/types.ts +42 -18
- package/src/engines/ensemble/index.ts +6 -0
- package/src/engines/ensemble/types.ts +17 -13
- package/src/engines/expert/index.ts +1 -0
- package/src/engines/expert/types.ts +11 -9
- package/src/engines/fuzzy/fuzzy.types.ts +65 -31
- package/src/engines/loyalty/index.ts +9 -0
- package/src/engines/loyalty/types.ts +36 -5
- package/src/engines/menu-engineering/compiler.ts +145 -0
- package/src/engines/menu-engineering/engine.ts +48 -0
- package/src/engines/menu-engineering/index.ts +47 -0
- package/src/engines/menu-engineering/strategy.ts +414 -0
- package/src/engines/menu-engineering/types.ts +242 -0
- package/src/engines/monte-carlo/index.ts +1 -0
- package/src/engines/monte-carlo/types.ts +16 -21
- package/src/engines/negotiation/index.ts +8 -0
- package/src/engines/negotiation/types.ts +23 -4
- package/src/engines/prediction/index.ts +5 -0
- package/src/engines/prediction/types.ts +35 -5
- package/src/engines/pricing/index.ts +3 -1
- package/src/engines/pricing/types.ts +17 -1
- package/src/engines/ranking/index.ts +5 -0
- package/src/engines/ranking/types.ts +32 -11
- package/src/engines/recipe-costing/compiler.ts +219 -0
- package/src/engines/recipe-costing/engine.ts +48 -0
- package/src/engines/recipe-costing/index.ts +48 -0
- package/src/engines/recipe-costing/strategy.ts +357 -0
- package/src/engines/recipe-costing/types.ts +269 -0
- package/src/engines/scoring/index.ts +2 -0
- package/src/engines/scoring/types.ts +8 -6
- package/src/engines/sentiment/index.ts +2 -0
- package/src/engines/sentiment/types.ts +13 -2
- package/src/engines/state-machine/index.ts +3 -0
- package/src/engines/state-machine/types.ts +8 -0
- package/src/engines/utility/index.ts +5 -0
- package/src/engines/utility/types.ts +23 -3
- package/src/index.ts +146 -8
|
@@ -59,27 +59,22 @@ export const ITERATION_PRESETS: Record<IterationPreset, number> = {
|
|
|
59
59
|
* Estimate type determines the probability distribution shape.
|
|
60
60
|
* Multiple aliases supported for different audiences.
|
|
61
61
|
*/
|
|
62
|
-
export
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// Discrete distribution (weighted choices)
|
|
80
|
-
| 'one-of' // Layperson-friendly
|
|
81
|
-
| 'weighted' // Technical
|
|
82
|
-
| 'choice'; // Alternative
|
|
62
|
+
export const EstimateTypes = {
|
|
63
|
+
bestLikelyWorst: 'best-likely-worst',
|
|
64
|
+
lowMidHigh: 'low-mid-high',
|
|
65
|
+
minExpectedMax: 'min-expected-max',
|
|
66
|
+
optimisticRealisticPessimistic: 'optimistic-realistic-pessimistic',
|
|
67
|
+
typicallyAround: 'typically-around',
|
|
68
|
+
bellCurve: 'bell-curve',
|
|
69
|
+
normal: 'normal',
|
|
70
|
+
anywhereFrom: 'anywhere-from',
|
|
71
|
+
uniform: 'uniform',
|
|
72
|
+
anyBetween: 'any-between',
|
|
73
|
+
oneOf: 'one-of',
|
|
74
|
+
weighted: 'weighted',
|
|
75
|
+
choice: 'choice',
|
|
76
|
+
} as const;
|
|
77
|
+
export type EstimateType = typeof EstimateTypes[keyof typeof EstimateTypes];
|
|
83
78
|
|
|
84
79
|
// ========================================
|
|
85
80
|
// Variable Definitions
|
|
@@ -32,6 +32,14 @@ export type {
|
|
|
32
32
|
NegotiationOptions
|
|
33
33
|
} from './types';
|
|
34
34
|
|
|
35
|
+
// Constants
|
|
36
|
+
export {
|
|
37
|
+
NegotiationStrategies,
|
|
38
|
+
ConcessionStyles,
|
|
39
|
+
DealOutcomes,
|
|
40
|
+
DimensionDirections
|
|
41
|
+
} from './types';
|
|
42
|
+
|
|
35
43
|
// Compiler
|
|
36
44
|
export { compileNegotiationRuleSet } from './compiler';
|
|
37
45
|
|
|
@@ -13,16 +13,35 @@ import type { SemanticPriority } from '../utility/types';
|
|
|
13
13
|
// ========================================
|
|
14
14
|
|
|
15
15
|
/** Negotiation algorithm to use */
|
|
16
|
-
export
|
|
16
|
+
export const NegotiationStrategies = {
|
|
17
|
+
zopaAnalysis: 'zopa-analysis',
|
|
18
|
+
concessionPlanning: 'concession-planning',
|
|
19
|
+
packageOptimization: 'package-optimization',
|
|
20
|
+
} as const;
|
|
21
|
+
export type NegotiationStrategy = typeof NegotiationStrategies[keyof typeof NegotiationStrategies];
|
|
17
22
|
|
|
18
23
|
/** Concession aggressiveness style */
|
|
19
|
-
export
|
|
24
|
+
export const ConcessionStyles = {
|
|
25
|
+
aggressive: 'aggressive',
|
|
26
|
+
moderate: 'moderate',
|
|
27
|
+
collaborative: 'collaborative',
|
|
28
|
+
} as const;
|
|
29
|
+
export type ConcessionStyle = typeof ConcessionStyles[keyof typeof ConcessionStyles];
|
|
20
30
|
|
|
21
31
|
/** Outcome of a negotiation analysis */
|
|
22
|
-
export
|
|
32
|
+
export const DealOutcomes = {
|
|
33
|
+
agreed: 'agreed',
|
|
34
|
+
noZopa: 'no-zopa',
|
|
35
|
+
batnaPreferred: 'batna-preferred',
|
|
36
|
+
} as const;
|
|
37
|
+
export type DealOutcome = typeof DealOutcomes[keyof typeof DealOutcomes];
|
|
23
38
|
|
|
24
39
|
/** Whether higher or lower values are preferred for a dimension */
|
|
25
|
-
export
|
|
40
|
+
export const DimensionDirections = {
|
|
41
|
+
higherIsBetter: 'higher-is-better',
|
|
42
|
+
lowerIsBetter: 'lower-is-better',
|
|
43
|
+
} as const;
|
|
44
|
+
export type DimensionDirection = typeof DimensionDirections[keyof typeof DimensionDirections];
|
|
26
45
|
|
|
27
46
|
// ========================================
|
|
28
47
|
// Dimension & Party Types
|
|
@@ -11,19 +11,49 @@
|
|
|
11
11
|
// ========================================
|
|
12
12
|
|
|
13
13
|
/** Prediction algorithm to use */
|
|
14
|
-
export
|
|
14
|
+
export const PredictionStrategies = {
|
|
15
|
+
linearRegression: 'linear-regression',
|
|
16
|
+
exponentialSmoothing: 'exponential-smoothing',
|
|
17
|
+
weightedMovingAverage: 'weighted-moving-average',
|
|
18
|
+
} as const;
|
|
19
|
+
export type PredictionStrategy = typeof PredictionStrategies[keyof typeof PredictionStrategies];
|
|
15
20
|
|
|
16
21
|
/** Trend direction derived from slope */
|
|
17
|
-
export
|
|
22
|
+
export const TrendDirections = {
|
|
23
|
+
rising: 'rising',
|
|
24
|
+
stable: 'stable',
|
|
25
|
+
declining: 'declining',
|
|
26
|
+
insufficientData: 'insufficient-data',
|
|
27
|
+
} as const;
|
|
28
|
+
export type TrendDirection = typeof TrendDirections[keyof typeof TrendDirections];
|
|
18
29
|
|
|
19
30
|
/** Exponential smoothing sensitivity presets */
|
|
20
|
-
export
|
|
31
|
+
export const SmoothingPresets = {
|
|
32
|
+
responsive: 'responsive',
|
|
33
|
+
balanced: 'balanced',
|
|
34
|
+
smooth: 'smooth',
|
|
35
|
+
} as const;
|
|
36
|
+
export type SmoothingPreset = typeof SmoothingPresets[keyof typeof SmoothingPresets];
|
|
21
37
|
|
|
22
38
|
/** Forecast horizon presets */
|
|
23
|
-
export
|
|
39
|
+
export const HorizonPresets = {
|
|
40
|
+
oneDay: '1-day',
|
|
41
|
+
oneWeek: '1-week',
|
|
42
|
+
oneMonth: '1-month',
|
|
43
|
+
threeMonths: '3-months',
|
|
44
|
+
sixMonths: '6-months',
|
|
45
|
+
oneYear: '1-year',
|
|
46
|
+
} as const;
|
|
47
|
+
export type HorizonPreset = typeof HorizonPresets[keyof typeof HorizonPresets];
|
|
24
48
|
|
|
25
49
|
/** Human-readable confidence bracket */
|
|
26
|
-
export
|
|
50
|
+
export const ConfidenceBrackets = {
|
|
51
|
+
high: 'high',
|
|
52
|
+
moderate: 'moderate',
|
|
53
|
+
low: 'low',
|
|
54
|
+
insufficientData: 'insufficient-data',
|
|
55
|
+
} as const;
|
|
56
|
+
export type ConfidenceBracket = typeof ConfidenceBrackets[keyof typeof ConfidenceBrackets];
|
|
27
57
|
|
|
28
58
|
// ========================================
|
|
29
59
|
// Preset Value Maps
|
|
@@ -72,6 +72,8 @@ export type {
|
|
|
72
72
|
ProductType,
|
|
73
73
|
|
|
74
74
|
// Pricing mode types
|
|
75
|
+
PricingModeName,
|
|
76
|
+
MetricFormat,
|
|
75
77
|
FlatPricing,
|
|
76
78
|
GraduatedTier,
|
|
77
79
|
GraduatedPricing,
|
|
@@ -129,7 +131,7 @@ export type {
|
|
|
129
131
|
} from './types';
|
|
130
132
|
|
|
131
133
|
// Currency utilities
|
|
132
|
-
export { CURRENCIES, getCurrencyInfo } from './types';
|
|
134
|
+
export { PricingModeNames, MetricFormats, CURRENCIES, getCurrencyInfo } from './types';
|
|
133
135
|
|
|
134
136
|
// Compiler
|
|
135
137
|
export { compilePricingRuleSet, compilePricingRule } from './compiler';
|
|
@@ -288,6 +288,15 @@ export type FormulaPricing = {
|
|
|
288
288
|
calculate: Operand;
|
|
289
289
|
};
|
|
290
290
|
|
|
291
|
+
export const PricingModeNames = {
|
|
292
|
+
flat: 'flat',
|
|
293
|
+
graduated: 'graduated',
|
|
294
|
+
volume: 'volume',
|
|
295
|
+
multiMeter: 'multi-meter',
|
|
296
|
+
formula: 'formula',
|
|
297
|
+
} as const;
|
|
298
|
+
export type PricingModeName = typeof PricingModeNames[keyof typeof PricingModeNames];
|
|
299
|
+
|
|
291
300
|
/**
|
|
292
301
|
* All pricing modes.
|
|
293
302
|
*/
|
|
@@ -377,6 +386,13 @@ export type Adjustment = DiscountAdjustment | MinimumAdjustment | MaximumAdjustm
|
|
|
377
386
|
// Metrics
|
|
378
387
|
// ========================================
|
|
379
388
|
|
|
389
|
+
export const MetricFormats = {
|
|
390
|
+
currency: 'currency',
|
|
391
|
+
percent: 'percent',
|
|
392
|
+
number: 'number',
|
|
393
|
+
} as const;
|
|
394
|
+
export type MetricFormat = typeof MetricFormats[keyof typeof MetricFormats];
|
|
395
|
+
|
|
380
396
|
/**
|
|
381
397
|
* Computed metric for comparison.
|
|
382
398
|
*/
|
|
@@ -388,7 +404,7 @@ export type Metric = {
|
|
|
388
404
|
/** Expression to compute the metric value */
|
|
389
405
|
calculate: Operand;
|
|
390
406
|
/** Display format */
|
|
391
|
-
format?:
|
|
407
|
+
format?: MetricFormat;
|
|
392
408
|
};
|
|
393
409
|
|
|
394
410
|
// ========================================
|
|
@@ -18,25 +18,46 @@ import type { SemanticPriority } from '../utility/types';
|
|
|
18
18
|
// ========================================
|
|
19
19
|
|
|
20
20
|
/** Ranking algorithm to use */
|
|
21
|
-
export
|
|
21
|
+
export const RankingStrategies = {
|
|
22
|
+
score: 'score',
|
|
23
|
+
elo: 'elo',
|
|
24
|
+
headToHead: 'head-to-head',
|
|
25
|
+
} as const;
|
|
26
|
+
export type RankingStrategy = typeof RankingStrategies[keyof typeof RankingStrategies];
|
|
22
27
|
|
|
23
28
|
/** Sort direction for rankings */
|
|
24
|
-
export
|
|
29
|
+
export const RankingDirections = {
|
|
30
|
+
highestFirst: 'highest-first',
|
|
31
|
+
lowestFirst: 'lowest-first',
|
|
32
|
+
} as const;
|
|
33
|
+
export type RankingDirection = typeof RankingDirections[keyof typeof RankingDirections];
|
|
25
34
|
|
|
26
35
|
/** Human-readable percentile bracket */
|
|
27
|
-
export
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
export const PercentileLabels = {
|
|
37
|
+
top1: 'top-1%',
|
|
38
|
+
top5: 'top-5%',
|
|
39
|
+
top10: 'top-10%',
|
|
40
|
+
top25: 'top-25%',
|
|
41
|
+
top50: 'top-50%',
|
|
42
|
+
bottomHalf: 'bottom-half',
|
|
43
|
+
} as const;
|
|
44
|
+
export type PercentileLabel = typeof PercentileLabels[keyof typeof PercentileLabels];
|
|
34
45
|
|
|
35
46
|
/** Rank movement since previous ranking */
|
|
36
|
-
export
|
|
47
|
+
export const Movements = {
|
|
48
|
+
climbed: 'climbed',
|
|
49
|
+
steady: 'steady',
|
|
50
|
+
dropped: 'dropped',
|
|
51
|
+
} as const;
|
|
52
|
+
export type Movement = typeof Movements[keyof typeof Movements];
|
|
37
53
|
|
|
38
54
|
/** Elo K-factor presets controlling rating volatility */
|
|
39
|
-
export
|
|
55
|
+
export const KFactorPresets = {
|
|
56
|
+
volatile: 'volatile',
|
|
57
|
+
standard: 'standard',
|
|
58
|
+
stable: 'stable',
|
|
59
|
+
} as const;
|
|
60
|
+
export type KFactorPreset = typeof KFactorPresets[keyof typeof KFactorPresets];
|
|
40
61
|
|
|
41
62
|
export const K_FACTOR_VALUES: Record<KFactorPreset, number> = {
|
|
42
63
|
'volatile': 32, // New entities, fast adjustment
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recipe Costing Engine Compiler
|
|
3
|
+
*
|
|
4
|
+
* Validates recipe costing rulesets and resolves defaults.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { CompilationError } from '../../core/errors';
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
RecipeCostingRuleSet,
|
|
11
|
+
CompiledRecipeCostingRuleSet,
|
|
12
|
+
CompiledPlateCostRuleSet,
|
|
13
|
+
CompiledMarginAnalysisRuleSet,
|
|
14
|
+
CompiledCostImpactRuleSet
|
|
15
|
+
} from './types';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Compile and validate a recipe costing ruleset.
|
|
19
|
+
*/
|
|
20
|
+
export function compileRecipeCostingRuleSet(
|
|
21
|
+
ruleSet: RecipeCostingRuleSet
|
|
22
|
+
): CompiledRecipeCostingRuleSet {
|
|
23
|
+
if (!ruleSet.id) {
|
|
24
|
+
throw new CompilationError('Recipe costing ruleset requires an id');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (ruleSet.mode !== 'recipe-costing') {
|
|
28
|
+
throw new CompilationError(`Expected mode 'recipe-costing', got '${ruleSet.mode}'`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!ruleSet.ingredients || ruleSet.ingredients.length === 0) {
|
|
32
|
+
throw new CompilationError('Recipe costing ruleset requires at least one ingredient');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!ruleSet.recipes || ruleSet.recipes.length === 0) {
|
|
36
|
+
throw new CompilationError('Recipe costing ruleset requires at least one recipe');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Validate unique ingredient IDs
|
|
40
|
+
const ingredientIds = new Set<string>();
|
|
41
|
+
for (const ing of ruleSet.ingredients) {
|
|
42
|
+
if (!ing.id) {
|
|
43
|
+
throw new CompilationError('Each ingredient requires an id');
|
|
44
|
+
}
|
|
45
|
+
if (ingredientIds.has(ing.id)) {
|
|
46
|
+
throw new CompilationError(`Duplicate ingredient id: '${ing.id}'`);
|
|
47
|
+
}
|
|
48
|
+
if (ing.purchaseQuantity <= 0) {
|
|
49
|
+
throw new CompilationError(`Ingredient '${ing.id}' purchaseQuantity must be positive`);
|
|
50
|
+
}
|
|
51
|
+
if (ing.purchaseCost <= 0) {
|
|
52
|
+
throw new CompilationError(`Ingredient '${ing.id}' purchaseCost must be positive`);
|
|
53
|
+
}
|
|
54
|
+
if (ing.yieldFactor !== undefined && (ing.yieldFactor <= 0 || ing.yieldFactor > 1)) {
|
|
55
|
+
throw new CompilationError(`Ingredient '${ing.id}' yieldFactor must be in (0, 1]`);
|
|
56
|
+
}
|
|
57
|
+
if (ing.wastePct !== undefined && (ing.wastePct < 0 || ing.wastePct >= 100)) {
|
|
58
|
+
throw new CompilationError(`Ingredient '${ing.id}' wastePct must be in [0, 100)`);
|
|
59
|
+
}
|
|
60
|
+
ingredientIds.add(ing.id);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Validate unique recipe IDs
|
|
64
|
+
const recipeIds = new Set<string>();
|
|
65
|
+
for (const recipe of ruleSet.recipes) {
|
|
66
|
+
if (!recipe.id) {
|
|
67
|
+
throw new CompilationError('Each recipe requires an id');
|
|
68
|
+
}
|
|
69
|
+
if (recipeIds.has(recipe.id)) {
|
|
70
|
+
throw new CompilationError(`Duplicate recipe id: '${recipe.id}'`);
|
|
71
|
+
}
|
|
72
|
+
recipeIds.add(recipe.id);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Validate recipe components reference valid ingredients or recipes
|
|
76
|
+
for (const recipe of ruleSet.recipes) {
|
|
77
|
+
for (const comp of recipe.components) {
|
|
78
|
+
if (comp.ingredientId && comp.subRecipeId) {
|
|
79
|
+
throw new CompilationError(
|
|
80
|
+
`Recipe '${recipe.id}' component references both ingredientId and subRecipeId. Must be one or the other.`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (!comp.ingredientId && !comp.subRecipeId) {
|
|
84
|
+
throw new CompilationError(
|
|
85
|
+
`Recipe '${recipe.id}' component must reference an ingredientId or subRecipeId`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (comp.ingredientId && !ingredientIds.has(comp.ingredientId)) {
|
|
89
|
+
throw new CompilationError(
|
|
90
|
+
`Recipe '${recipe.id}' references unknown ingredient: '${comp.ingredientId}'`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (comp.subRecipeId && !recipeIds.has(comp.subRecipeId)) {
|
|
94
|
+
throw new CompilationError(
|
|
95
|
+
`Recipe '${recipe.id}' references unknown sub-recipe: '${comp.subRecipeId}'`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Detect circular sub-recipe references
|
|
102
|
+
detectCircularRecipes(ruleSet.recipes);
|
|
103
|
+
|
|
104
|
+
switch (ruleSet.strategy) {
|
|
105
|
+
case 'plate-cost':
|
|
106
|
+
return compilePlateCost(ruleSet);
|
|
107
|
+
case 'margin-analysis':
|
|
108
|
+
return compileMarginAnalysis(ruleSet);
|
|
109
|
+
case 'cost-impact':
|
|
110
|
+
return compileCostImpact(ruleSet);
|
|
111
|
+
default:
|
|
112
|
+
throw new CompilationError(`Unknown recipe costing strategy: '${(ruleSet as any).strategy}'`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function compilePlateCost(
|
|
117
|
+
ruleSet: RecipeCostingRuleSet & { strategy: 'plate-cost' }
|
|
118
|
+
): CompiledPlateCostRuleSet {
|
|
119
|
+
return {
|
|
120
|
+
id: ruleSet.id,
|
|
121
|
+
name: ruleSet.name,
|
|
122
|
+
mode: 'recipe-costing',
|
|
123
|
+
strategy: 'plate-cost',
|
|
124
|
+
ingredients: ruleSet.ingredients,
|
|
125
|
+
recipes: ruleSet.recipes,
|
|
126
|
+
config: {
|
|
127
|
+
targetFoodCostPct: ruleSet.config?.targetFoodCostPct ?? 30
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function compileMarginAnalysis(
|
|
133
|
+
ruleSet: RecipeCostingRuleSet & { strategy: 'margin-analysis' }
|
|
134
|
+
): CompiledMarginAnalysisRuleSet {
|
|
135
|
+
return {
|
|
136
|
+
id: ruleSet.id,
|
|
137
|
+
name: ruleSet.name,
|
|
138
|
+
mode: 'recipe-costing',
|
|
139
|
+
strategy: 'margin-analysis',
|
|
140
|
+
ingredients: ruleSet.ingredients,
|
|
141
|
+
recipes: ruleSet.recipes,
|
|
142
|
+
config: {
|
|
143
|
+
targetFoodCostPct: ruleSet.config?.targetFoodCostPct ?? 30,
|
|
144
|
+
flagThreshold: ruleSet.config?.flagThreshold ?? 35
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function compileCostImpact(
|
|
150
|
+
ruleSet: RecipeCostingRuleSet & { strategy: 'cost-impact' }
|
|
151
|
+
): CompiledCostImpactRuleSet {
|
|
152
|
+
if (!ruleSet.config || !ruleSet.config.changes || ruleSet.config.changes.length === 0) {
|
|
153
|
+
throw new CompilationError('Cost-impact strategy requires at least one cost change');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const ingredientIds = new Set(ruleSet.ingredients.map(i => i.id));
|
|
157
|
+
for (const change of ruleSet.config.changes) {
|
|
158
|
+
if (!ingredientIds.has(change.ingredientId)) {
|
|
159
|
+
throw new CompilationError(`Cost change references unknown ingredient: '${change.ingredientId}'`);
|
|
160
|
+
}
|
|
161
|
+
if (change.newCost !== undefined && change.percentChange !== undefined) {
|
|
162
|
+
throw new CompilationError(
|
|
163
|
+
`Cost change for '${change.ingredientId}' must have newCost or percentChange, not both`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
if (change.newCost === undefined && change.percentChange === undefined) {
|
|
167
|
+
throw new CompilationError(
|
|
168
|
+
`Cost change for '${change.ingredientId}' must have newCost or percentChange`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
id: ruleSet.id,
|
|
175
|
+
name: ruleSet.name,
|
|
176
|
+
mode: 'recipe-costing',
|
|
177
|
+
strategy: 'cost-impact',
|
|
178
|
+
ingredients: ruleSet.ingredients,
|
|
179
|
+
recipes: ruleSet.recipes,
|
|
180
|
+
config: {
|
|
181
|
+
changes: ruleSet.config.changes,
|
|
182
|
+
targetFoodCostPct: ruleSet.config.targetFoodCostPct ?? 30
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function detectCircularRecipes(recipes: { id: string; components: { subRecipeId?: string }[] }[]): void {
|
|
188
|
+
const adjacency = new Map<string, string[]>();
|
|
189
|
+
for (const recipe of recipes) {
|
|
190
|
+
const deps: string[] = [];
|
|
191
|
+
for (const comp of recipe.components) {
|
|
192
|
+
if (comp.subRecipeId) {
|
|
193
|
+
deps.push(comp.subRecipeId);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
adjacency.set(recipe.id, deps);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const visited = new Set<string>();
|
|
200
|
+
const inStack = new Set<string>();
|
|
201
|
+
|
|
202
|
+
function dfs(id: string): void {
|
|
203
|
+
if (inStack.has(id)) {
|
|
204
|
+
throw new CompilationError(`Circular sub-recipe dependency detected involving '${id}'`);
|
|
205
|
+
}
|
|
206
|
+
if (visited.has(id)) return;
|
|
207
|
+
visited.add(id);
|
|
208
|
+
inStack.add(id);
|
|
209
|
+
const deps = adjacency.get(id) ?? [];
|
|
210
|
+
for (const dep of deps) {
|
|
211
|
+
dfs(dep);
|
|
212
|
+
}
|
|
213
|
+
inStack.delete(id);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
for (const recipe of recipes) {
|
|
217
|
+
dfs(recipe.id);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recipe Costing Engine
|
|
3
|
+
*
|
|
4
|
+
* Computes plate cost by rolling up ingredient costs through a bill of
|
|
5
|
+
* materials with yield factors and waste percentages. Supports margin
|
|
6
|
+
* analysis and cost-impact modeling when ingredient prices change.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const engine = new RecipeCostingEngine();
|
|
11
|
+
*
|
|
12
|
+
* const result = engine.execute(compiledRecipeCosting);
|
|
13
|
+
* // result.recipes -> per-recipe cost breakdowns
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
CompiledRecipeCostingRuleSet,
|
|
19
|
+
RecipeCostingOptions,
|
|
20
|
+
RecipeCostingResult
|
|
21
|
+
} from './types';
|
|
22
|
+
|
|
23
|
+
import { RecipeCostingExecutor } from './strategy';
|
|
24
|
+
|
|
25
|
+
export class RecipeCostingEngine {
|
|
26
|
+
private strategy: RecipeCostingExecutor;
|
|
27
|
+
|
|
28
|
+
constructor() {
|
|
29
|
+
this.strategy = new RecipeCostingExecutor();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Execute a recipe costing ruleset.
|
|
34
|
+
*
|
|
35
|
+
* Computes plate costs, analyzes margins, or models cost impacts
|
|
36
|
+
* depending on the strategy.
|
|
37
|
+
*
|
|
38
|
+
* @param ruleSet - Compiled recipe costing ruleset
|
|
39
|
+
* @param options - Runtime options
|
|
40
|
+
* @returns Recipe costing result
|
|
41
|
+
*/
|
|
42
|
+
execute(
|
|
43
|
+
ruleSet: CompiledRecipeCostingRuleSet,
|
|
44
|
+
options: RecipeCostingOptions = {}
|
|
45
|
+
): RecipeCostingResult {
|
|
46
|
+
return this.strategy.run(ruleSet, options);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recipe Costing Engine -- Plate Cost & Margin Analysis
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Types
|
|
6
|
+
export type {
|
|
7
|
+
RecipeCostingStrategy,
|
|
8
|
+
MeasurementUnit,
|
|
9
|
+
Ingredient,
|
|
10
|
+
RecipeComponent,
|
|
11
|
+
Recipe,
|
|
12
|
+
CostChange,
|
|
13
|
+
PlateCostConfig,
|
|
14
|
+
MarginAnalysisConfig,
|
|
15
|
+
CostImpactConfig,
|
|
16
|
+
PlateCostRuleSet,
|
|
17
|
+
MarginAnalysisRuleSet,
|
|
18
|
+
CostImpactRuleSet,
|
|
19
|
+
RecipeCostingRuleSet,
|
|
20
|
+
CompiledPlateCostRuleSet,
|
|
21
|
+
CompiledMarginAnalysisRuleSet,
|
|
22
|
+
CompiledCostImpactRuleSet,
|
|
23
|
+
CompiledRecipeCostingRuleSet,
|
|
24
|
+
IngredientCostBreakdown,
|
|
25
|
+
SubRecipeCostBreakdown,
|
|
26
|
+
RecipeCostResult,
|
|
27
|
+
CostImpactItem,
|
|
28
|
+
PlateCostResult,
|
|
29
|
+
MarginAnalysisResult,
|
|
30
|
+
CostImpactResult,
|
|
31
|
+
RecipeCostingResult,
|
|
32
|
+
RecipeCostingOptions
|
|
33
|
+
} from './types';
|
|
34
|
+
|
|
35
|
+
// Constants
|
|
36
|
+
export {
|
|
37
|
+
RecipeCostingStrategies,
|
|
38
|
+
MeasurementUnits
|
|
39
|
+
} from './types';
|
|
40
|
+
|
|
41
|
+
// Compiler
|
|
42
|
+
export { compileRecipeCostingRuleSet } from './compiler';
|
|
43
|
+
|
|
44
|
+
// Strategy
|
|
45
|
+
export { RecipeCostingExecutor, recipeCostingStrategy } from './strategy';
|
|
46
|
+
|
|
47
|
+
// Engine
|
|
48
|
+
export { RecipeCostingEngine } from './engine';
|