@higher.archi/boe 1.0.17 → 1.0.18
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/promotion/index.d.ts +2 -2
- package/dist/promotion/index.d.ts.map +1 -1
- package/dist/promotion/index.js +3 -1
- package/dist/promotion/index.js.map +1 -1
- package/dist/promotion/promotion.d.ts +33 -2
- package/dist/promotion/promotion.d.ts.map +1 -1
- package/dist/promotion/promotion.js +97 -1
- package/dist/promotion/promotion.js.map +1 -1
- package/dist/promotion/types.d.ts +8 -0
- package/dist/promotion/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/promotion/index.ts +2 -2
- package/src/promotion/promotion.ts +115 -2
- package/src/promotion/types.ts +10 -0
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type { PromotionOpportunity, PromotionTarget, PromotionResult } from './types';
|
|
2
|
-
export { promotionPath } from './promotion';
|
|
1
|
+
export type { PromotionOpportunity, PromotionTarget, PromotionResult, PromotionSummaryOptions, CategorySummaryOptions } from './types';
|
|
2
|
+
export { promotionPath, summarizePromotionPath, summarizeCategoryBreakdown } from './promotion';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/promotion/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/promotion/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,eAAe,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAGvI,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/promotion/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.promotionPath = void 0;
|
|
3
|
+
exports.summarizeCategoryBreakdown = exports.summarizePromotionPath = exports.promotionPath = void 0;
|
|
4
4
|
// Core promotion path
|
|
5
5
|
var promotion_1 = require("./promotion");
|
|
6
6
|
Object.defineProperty(exports, "promotionPath", { enumerable: true, get: function () { return promotion_1.promotionPath; } });
|
|
7
|
+
Object.defineProperty(exports, "summarizePromotionPath", { enumerable: true, get: function () { return promotion_1.summarizePromotionPath; } });
|
|
8
|
+
Object.defineProperty(exports, "summarizeCategoryBreakdown", { enumerable: true, get: function () { return promotion_1.summarizeCategoryBreakdown; } });
|
|
7
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/promotion/index.ts"],"names":[],"mappings":";;;AAGA,sBAAsB;AACtB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/promotion/index.ts"],"names":[],"mappings":";;;AAGA,sBAAsB;AACtB,yCAAgG;AAAvF,0GAAA,aAAa,OAAA;AAAE,mHAAA,sBAAsB,OAAA;AAAE,uHAAA,0BAA0B,OAAA"}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { CompiledScoringRuleSet, ScoringOptions, TierDefinition } from '../engines/scoring/types';
|
|
10
10
|
import type { FactInput } from '../core';
|
|
11
|
-
import type { PromotionTarget, PromotionResult } from './types';
|
|
11
|
+
import type { PromotionTarget, PromotionResult, PromotionSummaryOptions, CategorySummaryOptions } from './types';
|
|
12
12
|
/**
|
|
13
13
|
* Compute a promotion path — the ranked set of unfired rules that could
|
|
14
14
|
* close the gap between a current score and a target score or tier.
|
|
@@ -21,7 +21,7 @@ import type { PromotionTarget, PromotionResult } from './types';
|
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* ```typescript
|
|
24
|
-
* const path = promotionPath(
|
|
24
|
+
* const path = promotionPath(vendorRuleset, vendorFacts, { tier: 'preferred' });
|
|
25
25
|
*
|
|
26
26
|
* console.log(`Current: ${path.currentScore} (${path.currentTier})`);
|
|
27
27
|
* console.log(`Target: ${path.targetScore} (${path.targetTier})`);
|
|
@@ -34,4 +34,35 @@ import type { PromotionTarget, PromotionResult } from './types';
|
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
36
|
export declare function promotionPath<T extends TierDefinition = TierDefinition>(ruleset: CompiledScoringRuleSet<T>, facts: FactInput[], target: PromotionTarget, options?: ScoringOptions): PromotionResult<T>;
|
|
37
|
+
/**
|
|
38
|
+
* Generate a human-readable summary string from a PromotionResult.
|
|
39
|
+
*
|
|
40
|
+
* @param path - The promotion result to summarize
|
|
41
|
+
* @param options - Optional display settings
|
|
42
|
+
* @returns Formatted multi-line summary string
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const path = promotionPath(ruleset, facts, { tier: 'preferred' });
|
|
47
|
+
* console.log(summarizePromotionPath(path));
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function summarizePromotionPath(path: PromotionResult, options?: PromotionSummaryOptions): string;
|
|
51
|
+
/**
|
|
52
|
+
* Generate a human-readable category breakdown from a PromotionResult.
|
|
53
|
+
*
|
|
54
|
+
* Groups opportunities by category and shows per-category recoverable
|
|
55
|
+
* points with individual opportunity details.
|
|
56
|
+
*
|
|
57
|
+
* @param path - The promotion result to summarize
|
|
58
|
+
* @param options - Optional display settings
|
|
59
|
+
* @returns Formatted multi-line category breakdown string
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const path = promotionPath(ruleset, facts, { tier: 'elite' });
|
|
64
|
+
* console.log(summarizeCategoryBreakdown(path));
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function summarizeCategoryBreakdown(path: PromotionResult, options?: CategorySummaryOptions): string;
|
|
37
68
|
//# sourceMappingURL=promotion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promotion.d.ts","sourceRoot":"","sources":["../../src/promotion/promotion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,sBAAsB,EACtB,cAAc,EACd,cAAc,EACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAwB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"promotion.d.ts","sourceRoot":"","sources":["../../src/promotion/promotion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,sBAAsB,EACtB,cAAc,EACd,cAAc,EACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAwB,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEvI;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,EACrE,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC,EAClC,KAAK,EAAE,SAAS,EAAE,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,GAAE,cAAmB,GAC3B,eAAe,CAAC,CAAC,CAAC,CAmGpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,eAAe,EACrB,OAAO,GAAE,uBAA4B,GACpC,MAAM,CAqCR;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,eAAe,EACrB,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAqCR"}
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.promotionPath = promotionPath;
|
|
12
|
+
exports.summarizePromotionPath = summarizePromotionPath;
|
|
13
|
+
exports.summarizeCategoryBreakdown = summarizeCategoryBreakdown;
|
|
12
14
|
const engine_1 = require("../engines/scoring/engine");
|
|
13
15
|
/**
|
|
14
16
|
* Compute a promotion path — the ranked set of unfired rules that could
|
|
@@ -22,7 +24,7 @@ const engine_1 = require("../engines/scoring/engine");
|
|
|
22
24
|
*
|
|
23
25
|
* @example
|
|
24
26
|
* ```typescript
|
|
25
|
-
* const path = promotionPath(
|
|
27
|
+
* const path = promotionPath(vendorRuleset, vendorFacts, { tier: 'preferred' });
|
|
26
28
|
*
|
|
27
29
|
* console.log(`Current: ${path.currentScore} (${path.currentTier})`);
|
|
28
30
|
* console.log(`Target: ${path.targetScore} (${path.targetTier})`);
|
|
@@ -126,4 +128,98 @@ function promotionPath(ruleset, facts, target, options = {}) {
|
|
|
126
128
|
executionTimeMs,
|
|
127
129
|
};
|
|
128
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Generate a human-readable summary string from a PromotionResult.
|
|
133
|
+
*
|
|
134
|
+
* @param path - The promotion result to summarize
|
|
135
|
+
* @param options - Optional display settings
|
|
136
|
+
* @returns Formatted multi-line summary string
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* const path = promotionPath(ruleset, facts, { tier: 'preferred' });
|
|
141
|
+
* console.log(summarizePromotionPath(path));
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
function summarizePromotionPath(path, options = {}) {
|
|
145
|
+
const { maxOpportunities } = options;
|
|
146
|
+
const lines = [];
|
|
147
|
+
const currentLabel = path.currentTier ? `${path.currentScore} (${path.currentTier})` : `${path.currentScore}`;
|
|
148
|
+
const targetLabel = path.targetTier ? `${path.targetScore} (${path.targetTier})` : `${path.targetScore}`;
|
|
149
|
+
lines.push(`Current: ${currentLabel}`);
|
|
150
|
+
lines.push(`Target: ${targetLabel}`);
|
|
151
|
+
lines.push(`Gap: ${path.gap} points`);
|
|
152
|
+
lines.push(`Recoverable: ${path.totalRecoverable} points from ${path.opportunities.length} opportunities`);
|
|
153
|
+
if (path.alreadyMet) {
|
|
154
|
+
lines.push('');
|
|
155
|
+
lines.push('Target already met.');
|
|
156
|
+
return lines.join('\n');
|
|
157
|
+
}
|
|
158
|
+
const opps = maxOpportunities != null
|
|
159
|
+
? path.opportunities.slice(0, maxOpportunities)
|
|
160
|
+
: path.opportunities;
|
|
161
|
+
if (opps.length > 0) {
|
|
162
|
+
lines.push('');
|
|
163
|
+
for (const opp of opps) {
|
|
164
|
+
const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
|
|
165
|
+
lines.push(` [${impact}] ${opp.ruleName ?? opp.ruleId}`);
|
|
166
|
+
if (opp.reason)
|
|
167
|
+
lines.push(` ${opp.reason}`);
|
|
168
|
+
}
|
|
169
|
+
if (maxOpportunities != null && path.opportunities.length > maxOpportunities) {
|
|
170
|
+
const remaining = path.opportunities.length - maxOpportunities;
|
|
171
|
+
lines.push(` ... and ${remaining} more`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return lines.join('\n');
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Generate a human-readable category breakdown from a PromotionResult.
|
|
178
|
+
*
|
|
179
|
+
* Groups opportunities by category and shows per-category recoverable
|
|
180
|
+
* points with individual opportunity details.
|
|
181
|
+
*
|
|
182
|
+
* @param path - The promotion result to summarize
|
|
183
|
+
* @param options - Optional display settings
|
|
184
|
+
* @returns Formatted multi-line category breakdown string
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* const path = promotionPath(ruleset, facts, { tier: 'elite' });
|
|
189
|
+
* console.log(summarizeCategoryBreakdown(path));
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
function summarizeCategoryBreakdown(path, options = {}) {
|
|
193
|
+
const { maxPerCategory } = options;
|
|
194
|
+
const lines = [];
|
|
195
|
+
// Group by category
|
|
196
|
+
const byCategory = new Map();
|
|
197
|
+
for (const opp of path.opportunities) {
|
|
198
|
+
const cat = opp.category ?? '(uncategorized)';
|
|
199
|
+
if (!byCategory.has(cat))
|
|
200
|
+
byCategory.set(cat, []);
|
|
201
|
+
byCategory.get(cat).push(opp);
|
|
202
|
+
}
|
|
203
|
+
for (const [category, opps] of byCategory) {
|
|
204
|
+
const categoryTotal = opps.reduce((sum, o) => sum + (o.potentialScore ?? 0), 0);
|
|
205
|
+
lines.push(`${category} (${categoryTotal} recoverable points):`);
|
|
206
|
+
const displayed = maxPerCategory != null ? opps.slice(0, maxPerCategory) : opps;
|
|
207
|
+
for (const opp of displayed) {
|
|
208
|
+
const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
|
|
209
|
+
lines.push(` [${impact}] ${opp.ruleName ?? opp.ruleId}`);
|
|
210
|
+
if (opp.reason)
|
|
211
|
+
lines.push(` ${opp.reason}`);
|
|
212
|
+
}
|
|
213
|
+
if (maxPerCategory != null && opps.length > maxPerCategory) {
|
|
214
|
+
const remaining = opps.length - maxPerCategory;
|
|
215
|
+
lines.push(` ... and ${remaining} more`);
|
|
216
|
+
}
|
|
217
|
+
lines.push('');
|
|
218
|
+
}
|
|
219
|
+
// Remove trailing blank line
|
|
220
|
+
if (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
221
|
+
lines.pop();
|
|
222
|
+
}
|
|
223
|
+
return lines.join('\n');
|
|
224
|
+
}
|
|
129
225
|
//# sourceMappingURL=promotion.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promotion.js","sourceRoot":"","sources":["../../src/promotion/promotion.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAmCH,sCAwGC;
|
|
1
|
+
{"version":3,"file":"promotion.js","sourceRoot":"","sources":["../../src/promotion/promotion.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,6 +22,14 @@ export type PromotionTarget = {
|
|
|
22
22
|
score: number;
|
|
23
23
|
tier?: never;
|
|
24
24
|
};
|
|
25
|
+
/** Options for summarizePromotionPath */
|
|
26
|
+
export type PromotionSummaryOptions = {
|
|
27
|
+
maxOpportunities?: number;
|
|
28
|
+
};
|
|
29
|
+
/** Options for summarizeCategoryBreakdown */
|
|
30
|
+
export type CategorySummaryOptions = {
|
|
31
|
+
maxPerCategory?: number;
|
|
32
|
+
};
|
|
25
33
|
/** Promotion path result */
|
|
26
34
|
export type PromotionResult<T extends TierDefinition = TierDefinition> = {
|
|
27
35
|
currentScore: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/promotion/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE9E,+DAA+D;AAC/D,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,mEAAmE;AACnE,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAC/B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpC,4BAA4B;AAC5B,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAAI;IACvE,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/promotion/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE9E,+DAA+D;AAC/D,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,mEAAmE;AACnE,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAC/B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpC,yCAAyC;AACzC,MAAM,MAAM,uBAAuB,GAAG;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,6CAA6C;AAC7C,MAAM,MAAM,sBAAsB,GAAG;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,4BAA4B;AAC5B,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAAI;IACvE,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@higher.archi/boe",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "A multi-strategy rule engine supporting forward chaining, backward chaining, scoring, sequential, fuzzy logic, and Bayesian inference",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/src/promotion/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Types
|
|
2
|
-
export type { PromotionOpportunity, PromotionTarget, PromotionResult } from './types';
|
|
2
|
+
export type { PromotionOpportunity, PromotionTarget, PromotionResult, PromotionSummaryOptions, CategorySummaryOptions } from './types';
|
|
3
3
|
|
|
4
4
|
// Core promotion path
|
|
5
|
-
export { promotionPath } from './promotion';
|
|
5
|
+
export { promotionPath, summarizePromotionPath, summarizeCategoryBreakdown } from './promotion';
|
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
TierDefinition
|
|
15
15
|
} from '../engines/scoring/types';
|
|
16
16
|
import type { FactInput } from '../core';
|
|
17
|
-
import type { PromotionTarget, PromotionResult, PromotionOpportunity } from './types';
|
|
17
|
+
import type { PromotionTarget, PromotionResult, PromotionOpportunity, PromotionSummaryOptions, CategorySummaryOptions } from './types';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Compute a promotion path — the ranked set of unfired rules that could
|
|
@@ -28,7 +28,7 @@ import type { PromotionTarget, PromotionResult, PromotionOpportunity } from './t
|
|
|
28
28
|
*
|
|
29
29
|
* @example
|
|
30
30
|
* ```typescript
|
|
31
|
-
* const path = promotionPath(
|
|
31
|
+
* const path = promotionPath(vendorRuleset, vendorFacts, { tier: 'preferred' });
|
|
32
32
|
*
|
|
33
33
|
* console.log(`Current: ${path.currentScore} (${path.currentTier})`);
|
|
34
34
|
* console.log(`Target: ${path.targetScore} (${path.targetTier})`);
|
|
@@ -145,3 +145,116 @@ export function promotionPath<T extends TierDefinition = TierDefinition>(
|
|
|
145
145
|
executionTimeMs,
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Generate a human-readable summary string from a PromotionResult.
|
|
151
|
+
*
|
|
152
|
+
* @param path - The promotion result to summarize
|
|
153
|
+
* @param options - Optional display settings
|
|
154
|
+
* @returns Formatted multi-line summary string
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* const path = promotionPath(ruleset, facts, { tier: 'preferred' });
|
|
159
|
+
* console.log(summarizePromotionPath(path));
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export function summarizePromotionPath(
|
|
163
|
+
path: PromotionResult,
|
|
164
|
+
options: PromotionSummaryOptions = {}
|
|
165
|
+
): string {
|
|
166
|
+
const { maxOpportunities } = options;
|
|
167
|
+
const lines: string[] = [];
|
|
168
|
+
|
|
169
|
+
const currentLabel = path.currentTier ? `${path.currentScore} (${path.currentTier})` : `${path.currentScore}`;
|
|
170
|
+
const targetLabel = path.targetTier ? `${path.targetScore} (${path.targetTier})` : `${path.targetScore}`;
|
|
171
|
+
|
|
172
|
+
lines.push(`Current: ${currentLabel}`);
|
|
173
|
+
lines.push(`Target: ${targetLabel}`);
|
|
174
|
+
lines.push(`Gap: ${path.gap} points`);
|
|
175
|
+
lines.push(`Recoverable: ${path.totalRecoverable} points from ${path.opportunities.length} opportunities`);
|
|
176
|
+
|
|
177
|
+
if (path.alreadyMet) {
|
|
178
|
+
lines.push('');
|
|
179
|
+
lines.push('Target already met.');
|
|
180
|
+
return lines.join('\n');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const opps = maxOpportunities != null
|
|
184
|
+
? path.opportunities.slice(0, maxOpportunities)
|
|
185
|
+
: path.opportunities;
|
|
186
|
+
|
|
187
|
+
if (opps.length > 0) {
|
|
188
|
+
lines.push('');
|
|
189
|
+
for (const opp of opps) {
|
|
190
|
+
const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
|
|
191
|
+
lines.push(` [${impact}] ${opp.ruleName ?? opp.ruleId}`);
|
|
192
|
+
if (opp.reason) lines.push(` ${opp.reason}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (maxOpportunities != null && path.opportunities.length > maxOpportunities) {
|
|
196
|
+
const remaining = path.opportunities.length - maxOpportunities;
|
|
197
|
+
lines.push(` ... and ${remaining} more`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return lines.join('\n');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Generate a human-readable category breakdown from a PromotionResult.
|
|
206
|
+
*
|
|
207
|
+
* Groups opportunities by category and shows per-category recoverable
|
|
208
|
+
* points with individual opportunity details.
|
|
209
|
+
*
|
|
210
|
+
* @param path - The promotion result to summarize
|
|
211
|
+
* @param options - Optional display settings
|
|
212
|
+
* @returns Formatted multi-line category breakdown string
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```typescript
|
|
216
|
+
* const path = promotionPath(ruleset, facts, { tier: 'elite' });
|
|
217
|
+
* console.log(summarizeCategoryBreakdown(path));
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
export function summarizeCategoryBreakdown(
|
|
221
|
+
path: PromotionResult,
|
|
222
|
+
options: CategorySummaryOptions = {}
|
|
223
|
+
): string {
|
|
224
|
+
const { maxPerCategory } = options;
|
|
225
|
+
const lines: string[] = [];
|
|
226
|
+
|
|
227
|
+
// Group by category
|
|
228
|
+
const byCategory = new Map<string, PromotionOpportunity[]>();
|
|
229
|
+
for (const opp of path.opportunities) {
|
|
230
|
+
const cat = opp.category ?? '(uncategorized)';
|
|
231
|
+
if (!byCategory.has(cat)) byCategory.set(cat, []);
|
|
232
|
+
byCategory.get(cat)!.push(opp);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const [category, opps] of byCategory) {
|
|
236
|
+
const categoryTotal = opps.reduce((sum, o) => sum + (o.potentialScore ?? 0), 0);
|
|
237
|
+
lines.push(`${category} (${categoryTotal} recoverable points):`);
|
|
238
|
+
|
|
239
|
+
const displayed = maxPerCategory != null ? opps.slice(0, maxPerCategory) : opps;
|
|
240
|
+
for (const opp of displayed) {
|
|
241
|
+
const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
|
|
242
|
+
lines.push(` [${impact}] ${opp.ruleName ?? opp.ruleId}`);
|
|
243
|
+
if (opp.reason) lines.push(` ${opp.reason}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (maxPerCategory != null && opps.length > maxPerCategory) {
|
|
247
|
+
const remaining = opps.length - maxPerCategory;
|
|
248
|
+
lines.push(` ... and ${remaining} more`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
lines.push('');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Remove trailing blank line
|
|
255
|
+
if (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
256
|
+
lines.pop();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return lines.join('\n');
|
|
260
|
+
}
|
package/src/promotion/types.ts
CHANGED
|
@@ -22,6 +22,16 @@ export type PromotionTarget =
|
|
|
22
22
|
| { tier: string; score?: never }
|
|
23
23
|
| { score: number; tier?: never };
|
|
24
24
|
|
|
25
|
+
/** Options for summarizePromotionPath */
|
|
26
|
+
export type PromotionSummaryOptions = {
|
|
27
|
+
maxOpportunities?: number; // max opportunities to display (default: all)
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Options for summarizeCategoryBreakdown */
|
|
31
|
+
export type CategorySummaryOptions = {
|
|
32
|
+
maxPerCategory?: number; // max opportunities per category (default: all)
|
|
33
|
+
};
|
|
34
|
+
|
|
25
35
|
/** Promotion path result */
|
|
26
36
|
export type PromotionResult<T extends TierDefinition = TierDefinition> = {
|
|
27
37
|
currentScore: number;
|