@orkestrel/rater 0.0.12 → 0.0.13
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/README.md +12 -8
- package/dist/src/core/index.cjs +127 -127
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +256 -253
- package/dist/src/core/index.d.ts +256 -253
- package/dist/src/core/index.js +120 -120
- package/dist/src/core/index.js.map +1 -1
- package/package.json +20 -16
package/dist/src/core/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@ let _orkestrel_reason = require("@orkestrel/reason");
|
|
|
4
4
|
let _orkestrel_emitter = require("@orkestrel/emitter");
|
|
5
5
|
//#region src/core/errors.ts
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Represents a coded programmer error thrown by the rating layer.
|
|
8
8
|
*
|
|
9
9
|
* @remarks
|
|
10
10
|
* `DEFINITION` — the `rate` input failed both the array-of-lines and rating
|
|
@@ -29,10 +29,10 @@ var RaterError = class extends Error {
|
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
32
|
+
* Narrows a caught value to a {@link RaterError}.
|
|
33
33
|
*
|
|
34
34
|
* @param value - The caught value to test
|
|
35
|
-
* @returns
|
|
35
|
+
* @returns True if `value` is a `RaterError`; false otherwise
|
|
36
36
|
*
|
|
37
37
|
* @example
|
|
38
38
|
* ```ts
|
|
@@ -51,10 +51,10 @@ function isRaterError(value) {
|
|
|
51
51
|
//#endregion
|
|
52
52
|
//#region src/core/validators.ts
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
54
|
+
* Determines whether a value is a {@link Stage} literal.
|
|
55
55
|
*
|
|
56
56
|
* @param value - The value to test
|
|
57
|
-
* @returns
|
|
57
|
+
* @returns True if `value` is `'factor'`, `'group'`, or `'total'`; false otherwise
|
|
58
58
|
*
|
|
59
59
|
* @example
|
|
60
60
|
* ```ts
|
|
@@ -66,21 +66,21 @@ function isRaterError(value) {
|
|
|
66
66
|
*/
|
|
67
67
|
var isStage = (0, _orkestrel_contract.literalOf)("factor", "group", "total");
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
69
|
+
* Determines whether a value is an exact {@link LineDefinition} record.
|
|
70
70
|
*
|
|
71
71
|
* @remarks
|
|
72
|
-
* Total guard
|
|
73
|
-
*
|
|
72
|
+
* Total guard: adversarial input (cycles, hostile prototypes) returns `false`,
|
|
73
|
+
* never throws. The record shape is EXACT — an extra key fails.
|
|
74
74
|
*
|
|
75
75
|
* @param value - The value to test
|
|
76
|
-
* @returns
|
|
76
|
+
* @returns True if `value` is a `LineDefinition`; false otherwise
|
|
77
77
|
*
|
|
78
78
|
* @example
|
|
79
79
|
* ```ts
|
|
80
|
-
* import {
|
|
80
|
+
* import { createQuantitativeDefinition } from '@orkestrel/reason'
|
|
81
81
|
* import { isLineDefinition } from '@orkestrel/rater'
|
|
82
82
|
*
|
|
83
|
-
* isLineDefinition({ id: 'base', name: 'Base', rate:
|
|
83
|
+
* isLineDefinition({ id: 'base', name: 'Base', rate: createQuantitativeDefinition('base', 'Base', []) }) // true
|
|
84
84
|
* ```
|
|
85
85
|
*/
|
|
86
86
|
function isLineDefinition(value) {
|
|
@@ -93,14 +93,14 @@ function isLineDefinition(value) {
|
|
|
93
93
|
}, ["description", "metadata"])(value);
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
96
|
-
*
|
|
96
|
+
* Determines whether a value is an exact {@link RatingDefinition} record.
|
|
97
97
|
*
|
|
98
98
|
* @remarks
|
|
99
|
-
* Total guard
|
|
100
|
-
*
|
|
99
|
+
* Total guard: adversarial input (cycles, hostile prototypes) returns `false`,
|
|
100
|
+
* never throws. The record shape is EXACT — an extra key fails.
|
|
101
101
|
*
|
|
102
102
|
* @param value - The value to test
|
|
103
|
-
* @returns
|
|
103
|
+
* @returns True if `value` is a `RatingDefinition`; false otherwise
|
|
104
104
|
*
|
|
105
105
|
* @example
|
|
106
106
|
* ```ts
|
|
@@ -119,7 +119,7 @@ function isRatingDefinition(value) {
|
|
|
119
119
|
}, ["description", "metadata"])(value);
|
|
120
120
|
}
|
|
121
121
|
/**
|
|
122
|
-
*
|
|
122
|
+
* Determines whether a value is an open result-side {@link Evidence} object.
|
|
123
123
|
*
|
|
124
124
|
* @remarks
|
|
125
125
|
* Unknown members, prototypes, and class instances are admitted. Arrays are
|
|
@@ -128,7 +128,7 @@ function isRatingDefinition(value) {
|
|
|
128
128
|
* members rather than requiring or enumerating own keys.
|
|
129
129
|
*
|
|
130
130
|
* @param value - The value to test
|
|
131
|
-
* @returns
|
|
131
|
+
* @returns True if every checked `Evidence` member conforms; false otherwise
|
|
132
132
|
*
|
|
133
133
|
* @example
|
|
134
134
|
* ```ts
|
|
@@ -151,14 +151,14 @@ function isEvidence(value) {
|
|
|
151
151
|
])(value);
|
|
152
152
|
}
|
|
153
153
|
/**
|
|
154
|
-
*
|
|
154
|
+
* Determines whether a value is an open {@link WorksheetFactor} result object.
|
|
155
155
|
*
|
|
156
156
|
* @remarks
|
|
157
157
|
* Unknown members, prototypes, and class instances are admitted. Arrays are
|
|
158
158
|
* refused. Optional members may be absent or read as `undefined`.
|
|
159
159
|
*
|
|
160
160
|
* @param value - The value to test
|
|
161
|
-
* @returns
|
|
161
|
+
* @returns True if every published worksheet-factor member conforms; false otherwise
|
|
162
162
|
*
|
|
163
163
|
* @example
|
|
164
164
|
* ```ts
|
|
@@ -182,14 +182,14 @@ function isWorksheetFactor(value) {
|
|
|
182
182
|
])(value);
|
|
183
183
|
}
|
|
184
184
|
/**
|
|
185
|
-
*
|
|
185
|
+
* Determines whether a value is an open {@link WorksheetGroup} result object.
|
|
186
186
|
*
|
|
187
187
|
* @remarks
|
|
188
188
|
* Unknown members, prototypes, and class instances are admitted. Arrays are
|
|
189
189
|
* refused. Optional members may be absent or read as `undefined`.
|
|
190
190
|
*
|
|
191
191
|
* @param value - The value to test
|
|
192
|
-
* @returns
|
|
192
|
+
* @returns True if every published worksheet-group member conforms; false otherwise
|
|
193
193
|
*
|
|
194
194
|
* @example
|
|
195
195
|
* ```ts
|
|
@@ -209,14 +209,14 @@ function isWorksheetGroup(value) {
|
|
|
209
209
|
}, ["name", "description"])(value);
|
|
210
210
|
}
|
|
211
211
|
/**
|
|
212
|
-
*
|
|
212
|
+
* Determines whether a value is an open {@link Step} result object.
|
|
213
213
|
*
|
|
214
214
|
* @remarks
|
|
215
215
|
* Unknown members, prototypes, and class instances are admitted. Arrays are
|
|
216
216
|
* refused. Optional members may be absent or read as `undefined`.
|
|
217
217
|
*
|
|
218
218
|
* @param value - The value to test
|
|
219
|
-
* @returns
|
|
219
|
+
* @returns True if every published worksheet-step member conforms; false otherwise
|
|
220
220
|
*
|
|
221
221
|
* @example
|
|
222
222
|
* ```ts
|
|
@@ -239,14 +239,14 @@ function isStep(value) {
|
|
|
239
239
|
])(value);
|
|
240
240
|
}
|
|
241
241
|
/**
|
|
242
|
-
*
|
|
242
|
+
* Determines whether a value is an open {@link Worksheet} result object.
|
|
243
243
|
*
|
|
244
244
|
* @remarks
|
|
245
245
|
* Unknown members, prototypes, and class instances are admitted. Arrays are
|
|
246
246
|
* refused. Optional members may be absent or read as `undefined`.
|
|
247
247
|
*
|
|
248
248
|
* @param value - The value to test
|
|
249
|
-
* @returns
|
|
249
|
+
* @returns True if every published worksheet member conforms; false otherwise
|
|
250
250
|
*
|
|
251
251
|
* @example
|
|
252
252
|
* ```ts
|
|
@@ -270,21 +270,22 @@ function isWorksheet(value) {
|
|
|
270
270
|
}, ["precision"])(value);
|
|
271
271
|
}
|
|
272
272
|
/**
|
|
273
|
-
*
|
|
273
|
+
* Determines whether a value is an open {@link LineResult} object.
|
|
274
274
|
*
|
|
275
275
|
* @remarks
|
|
276
276
|
* Unknown members, prototypes, and class instances are admitted. Arrays are
|
|
277
277
|
* refused. `amount` may be absent or read as `undefined`; when present it stays
|
|
278
|
-
* a plain JavaScript `number` without a finite or range refinement.
|
|
278
|
+
* a plain JavaScript `number` without a finite or range refinement. The line's
|
|
279
|
+
* outcome lives on `worksheet`, so `worksheet.success` carries it.
|
|
279
280
|
*
|
|
280
281
|
* @param value - The value to test
|
|
281
|
-
* @returns
|
|
282
|
+
* @returns True if every published line-result member conforms; false otherwise
|
|
282
283
|
*
|
|
283
284
|
* @example
|
|
284
285
|
* ```ts
|
|
285
286
|
* import { isLineResult } from '@orkestrel/rater'
|
|
286
287
|
*
|
|
287
|
-
* isLineResult({ id: 'base', name: 'Base', worksheet: { id: 'base', name: 'Base', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: false }
|
|
288
|
+
* isLineResult({ id: 'base', name: 'Base', worksheet: { id: 'base', name: 'Base', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: false } }) // true
|
|
288
289
|
* ```
|
|
289
290
|
*/
|
|
290
291
|
function isLineResult(value) {
|
|
@@ -292,12 +293,11 @@ function isLineResult(value) {
|
|
|
292
293
|
id: _orkestrel_contract.isString,
|
|
293
294
|
name: _orkestrel_contract.isString,
|
|
294
295
|
amount: _orkestrel_contract.isNumber,
|
|
295
|
-
worksheet: isWorksheet
|
|
296
|
-
success: _orkestrel_contract.isBoolean
|
|
296
|
+
worksheet: isWorksheet
|
|
297
297
|
}, ["amount"])(value);
|
|
298
298
|
}
|
|
299
299
|
/**
|
|
300
|
-
*
|
|
300
|
+
* Determines whether a value is an open {@link RatingResult} object.
|
|
301
301
|
*
|
|
302
302
|
* @remarks
|
|
303
303
|
* Use this guard for a result returned by a borrowed `RaterInterface`. Unknown
|
|
@@ -306,7 +306,7 @@ function isLineResult(value) {
|
|
|
306
306
|
* JavaScript `number` without a finite or range refinement.
|
|
307
307
|
*
|
|
308
308
|
* @param value - The value to test
|
|
309
|
-
* @returns
|
|
309
|
+
* @returns True if every published rating-result member conforms; false otherwise
|
|
310
310
|
*
|
|
311
311
|
* @example
|
|
312
312
|
* ```ts
|
|
@@ -325,7 +325,7 @@ function isRatingResult(value) {
|
|
|
325
325
|
//#endregion
|
|
326
326
|
//#region src/core/helpers.ts
|
|
327
327
|
/**
|
|
328
|
-
*
|
|
328
|
+
* Builds a {@link LineDefinition}.
|
|
329
329
|
*
|
|
330
330
|
* @param id - The line id
|
|
331
331
|
* @param name - The display name
|
|
@@ -335,13 +335,13 @@ function isRatingResult(value) {
|
|
|
335
335
|
*
|
|
336
336
|
* @example
|
|
337
337
|
* ```ts
|
|
338
|
-
* import {
|
|
339
|
-
* import {
|
|
338
|
+
* import { createQuantitativeDefinition } from '@orkestrel/reason'
|
|
339
|
+
* import { buildLineDefinition } from '@orkestrel/rater'
|
|
340
340
|
*
|
|
341
|
-
*
|
|
341
|
+
* buildLineDefinition('base', 'Base Amount', createQuantitativeDefinition('base', 'Base', []))
|
|
342
342
|
* ```
|
|
343
343
|
*/
|
|
344
|
-
function
|
|
344
|
+
function buildLineDefinition(id, name, rate, overrides) {
|
|
345
345
|
return {
|
|
346
346
|
id,
|
|
347
347
|
name,
|
|
@@ -350,7 +350,7 @@ function lineDefinition(id, name, rate, overrides) {
|
|
|
350
350
|
};
|
|
351
351
|
}
|
|
352
352
|
/**
|
|
353
|
-
*
|
|
353
|
+
* Builds a {@link RatingDefinition}.
|
|
354
354
|
*
|
|
355
355
|
* @param id - The rating id
|
|
356
356
|
* @param name - The display name
|
|
@@ -360,15 +360,15 @@ function lineDefinition(id, name, rate, overrides) {
|
|
|
360
360
|
*
|
|
361
361
|
* @example
|
|
362
362
|
* ```ts
|
|
363
|
-
* import {
|
|
364
|
-
* import {
|
|
363
|
+
* import { buildLineDefinition, buildRatingDefinition } from '@orkestrel/rater'
|
|
364
|
+
* import { createQuantitativeDefinition } from '@orkestrel/reason'
|
|
365
365
|
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
366
|
+
* buildRatingDefinition('r1', 'Rating', [
|
|
367
|
+
* buildLineDefinition('base', 'Base Amount', createQuantitativeDefinition('base', 'Base', [])),
|
|
368
368
|
* ])
|
|
369
369
|
* ```
|
|
370
370
|
*/
|
|
371
|
-
function
|
|
371
|
+
function buildRatingDefinition(id, name, lines, overrides) {
|
|
372
372
|
return {
|
|
373
373
|
id,
|
|
374
374
|
name,
|
|
@@ -377,24 +377,25 @@ function ratingDefinition(id, name, lines, overrides) {
|
|
|
377
377
|
};
|
|
378
378
|
}
|
|
379
379
|
/**
|
|
380
|
-
*
|
|
380
|
+
* Builds an {@link Evidence} row from an evaluated {@link Check}.
|
|
381
381
|
*
|
|
382
382
|
* @param check - The authored check
|
|
383
383
|
* @param actual - The resolved subject value
|
|
384
|
-
* @param met -
|
|
384
|
+
* @param met - If `true`, the check was met; if `false`, it was not; absent when the check
|
|
385
|
+
* was not yet evaluated
|
|
385
386
|
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
386
387
|
* @returns A fresh evidence row
|
|
387
388
|
*
|
|
388
389
|
* @example
|
|
389
390
|
* ```ts
|
|
390
|
-
* import {
|
|
391
|
-
* import {
|
|
391
|
+
* import { createCheck } from '@orkestrel/reason'
|
|
392
|
+
* import { buildEvidence } from '@orkestrel/rater'
|
|
392
393
|
*
|
|
393
|
-
* const entry =
|
|
394
|
-
*
|
|
394
|
+
* const entry = createCheck('age', 'above', 18)
|
|
395
|
+
* buildEvidence(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
|
|
395
396
|
* ```
|
|
396
397
|
*/
|
|
397
|
-
function
|
|
398
|
+
function buildEvidence(check, actual, met, labels) {
|
|
398
399
|
const field = (0, _orkestrel_reason.formatField)(check.field);
|
|
399
400
|
return {
|
|
400
401
|
field: check.field,
|
|
@@ -406,8 +407,8 @@ function evidenceCheck(check, actual, met, labels) {
|
|
|
406
407
|
};
|
|
407
408
|
}
|
|
408
409
|
/**
|
|
409
|
-
*
|
|
410
|
-
*
|
|
410
|
+
* Builds one evidence row per authored check of a quantitative factor, joined
|
|
411
|
+
* to that check's evaluated result.
|
|
411
412
|
*
|
|
412
413
|
* @param checks - The factor's authored checks
|
|
413
414
|
* @param results - The corresponding {@link CheckResult}s, in the same order
|
|
@@ -416,21 +417,21 @@ function evidenceCheck(check, actual, met, labels) {
|
|
|
416
417
|
*
|
|
417
418
|
* @example
|
|
418
419
|
* ```ts
|
|
419
|
-
* import {
|
|
420
|
-
* import {
|
|
420
|
+
* import { createCheck } from '@orkestrel/reason'
|
|
421
|
+
* import { buildEvidenceRows } from '@orkestrel/rater'
|
|
421
422
|
*
|
|
422
|
-
* const checks = [
|
|
423
|
-
*
|
|
423
|
+
* const checks = [createCheck('age', 'above', 18)]
|
|
424
|
+
* buildEvidenceRows(checks, [{ field: 'age', met: true, actual: 25 }])
|
|
424
425
|
* ```
|
|
425
426
|
*/
|
|
426
|
-
function
|
|
427
|
+
function buildEvidenceRows(checks, results, labels) {
|
|
427
428
|
return (checks ?? []).map((check, index) => {
|
|
428
429
|
const result = results?.[index];
|
|
429
|
-
return
|
|
430
|
+
return buildEvidence(check, result?.actual, result?.met, labels);
|
|
430
431
|
});
|
|
431
432
|
}
|
|
432
433
|
/**
|
|
433
|
-
*
|
|
434
|
+
* Joins one authored quantitative factor to its evaluated {@link FactorResult}.
|
|
434
435
|
*
|
|
435
436
|
* @param definition - The authored factor
|
|
436
437
|
* @param results - The group's factor results
|
|
@@ -439,14 +440,14 @@ function checkEvidence(checks, results, labels) {
|
|
|
439
440
|
*
|
|
440
441
|
* @example
|
|
441
442
|
* ```ts
|
|
442
|
-
* import {
|
|
443
|
-
* import {
|
|
443
|
+
* import { createFieldFactor } from '@orkestrel/reason'
|
|
444
|
+
* import { buildWorksheetFactor } from '@orkestrel/rater'
|
|
444
445
|
*
|
|
445
|
-
* const factor =
|
|
446
|
-
*
|
|
446
|
+
* const factor = createFieldFactor('seats', 'seats')
|
|
447
|
+
* buildWorksheetFactor(factor, [{ id: 'seats', applied: true, value: 10 }])
|
|
447
448
|
* ```
|
|
448
449
|
*/
|
|
449
|
-
function
|
|
450
|
+
function buildWorksheetFactor(definition, results, labels) {
|
|
450
451
|
const result = results.find((entry) => entry.id === definition.id);
|
|
451
452
|
return {
|
|
452
453
|
id: definition.id,
|
|
@@ -454,11 +455,11 @@ function worksheetFactor(definition, results, labels) {
|
|
|
454
455
|
...definition.description === void 0 ? {} : { description: definition.description },
|
|
455
456
|
applied: result?.applied ?? false,
|
|
456
457
|
...result?.value === void 0 ? {} : { value: result.value },
|
|
457
|
-
evidence:
|
|
458
|
+
evidence: buildEvidenceRows(definition.checks, result?.checks, labels)
|
|
458
459
|
};
|
|
459
460
|
}
|
|
460
461
|
/**
|
|
461
|
-
*
|
|
462
|
+
* Joins one authored quantitative group to its evaluated {@link GroupResult}.
|
|
462
463
|
*
|
|
463
464
|
* @param definition - The authored group
|
|
464
465
|
* @param results - The definition's group results
|
|
@@ -467,14 +468,14 @@ function worksheetFactor(definition, results, labels) {
|
|
|
467
468
|
*
|
|
468
469
|
* @example
|
|
469
470
|
* ```ts
|
|
470
|
-
* import {
|
|
471
|
-
* import {
|
|
471
|
+
* import { createFactorGroup, createStaticFactor } from '@orkestrel/reason'
|
|
472
|
+
* import { buildWorksheetGroup } from '@orkestrel/rater'
|
|
472
473
|
*
|
|
473
|
-
* const group =
|
|
474
|
-
*
|
|
474
|
+
* const group = createFactorGroup('charge', 'sum', [createStaticFactor('base', 100)])
|
|
475
|
+
* buildWorksheetGroup(group, [{ id: 'charge', applied: true, value: 100, factors: [] }])
|
|
475
476
|
* ```
|
|
476
477
|
*/
|
|
477
|
-
function
|
|
478
|
+
function buildWorksheetGroup(definition, results, labels) {
|
|
478
479
|
const result = results.find((entry) => entry.id === definition.id);
|
|
479
480
|
return {
|
|
480
481
|
id: definition.id,
|
|
@@ -482,11 +483,11 @@ function worksheetGroup(definition, results, labels) {
|
|
|
482
483
|
...definition.description === void 0 ? {} : { description: definition.description },
|
|
483
484
|
applied: result?.applied ?? false,
|
|
484
485
|
value: result?.value ?? 0,
|
|
485
|
-
factors: definition.factors.map((factor) =>
|
|
486
|
+
factors: definition.factors.map((factor) => buildWorksheetFactor(factor, result?.factors ?? [], labels))
|
|
486
487
|
};
|
|
487
488
|
}
|
|
488
489
|
/**
|
|
489
|
-
*
|
|
490
|
+
* Builds one display-neutral {@link Step} row.
|
|
490
491
|
*
|
|
491
492
|
* @param stage - The step's stage
|
|
492
493
|
* @param id - The stage's authored id, when it has one
|
|
@@ -497,13 +498,13 @@ function worksheetGroup(definition, results, labels) {
|
|
|
497
498
|
*
|
|
498
499
|
* @example
|
|
499
500
|
* ```ts
|
|
500
|
-
* import {
|
|
501
|
+
* import { buildWorksheetStep } from '@orkestrel/rater'
|
|
501
502
|
*
|
|
502
|
-
*
|
|
503
|
+
* buildWorksheetStep('total', 'quote', 'Quote', 110, 'sum = 110')
|
|
503
504
|
* // { stage: 'total', id: 'quote', name: 'Quote', value: 110, expression: 'sum = 110' }
|
|
504
505
|
* ```
|
|
505
506
|
*/
|
|
506
|
-
function
|
|
507
|
+
function buildWorksheetStep(stage, id, name, value, expression) {
|
|
507
508
|
return {
|
|
508
509
|
stage,
|
|
509
510
|
...id === void 0 ? {} : { id },
|
|
@@ -513,7 +514,7 @@ function worksheetStep(stage, id, name, value, expression) {
|
|
|
513
514
|
};
|
|
514
515
|
}
|
|
515
516
|
/**
|
|
516
|
-
*
|
|
517
|
+
* Builds the ordered {@link Step} rows for a resolved {@link Worksheet}.
|
|
517
518
|
*
|
|
518
519
|
* @param definition - The authored quantitative definition
|
|
519
520
|
* @param result - The evaluated quantitative result
|
|
@@ -522,27 +523,27 @@ function worksheetStep(stage, id, name, value, expression) {
|
|
|
522
523
|
*
|
|
523
524
|
* @example
|
|
524
525
|
* ```ts
|
|
525
|
-
* import {
|
|
526
|
-
* import {
|
|
526
|
+
* import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'
|
|
527
|
+
* import { buildWorksheetGroup, buildWorksheetSteps } from '@orkestrel/rater'
|
|
527
528
|
*
|
|
528
|
-
* const definition =
|
|
529
|
+
* const definition = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])
|
|
529
530
|
* const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)
|
|
530
531
|
* if (result.reasoning === 'quantitative') {
|
|
531
|
-
*
|
|
532
|
+
* buildWorksheetSteps(definition, result, definition.groups.map((group) => buildWorksheetGroup(group, result.groups)))
|
|
532
533
|
* }
|
|
533
534
|
* ```
|
|
534
535
|
*/
|
|
535
|
-
function
|
|
536
|
+
function buildWorksheetSteps(definition, result, groups) {
|
|
536
537
|
const output = [];
|
|
537
538
|
for (const group of groups) {
|
|
538
|
-
for (const factor of group.factors) if (factor.applied && factor.value !== void 0) output.push(
|
|
539
|
-
output.push(
|
|
539
|
+
for (const factor of group.factors) if (factor.applied && factor.value !== void 0) output.push(buildWorksheetStep("factor", factor.id, factor.name, factor.value, `${factor.id} = ${factor.value}`));
|
|
540
|
+
output.push(buildWorksheetStep("group", group.id, group.name, group.value, `${group.id} = ${group.value}`));
|
|
540
541
|
}
|
|
541
|
-
output.push(
|
|
542
|
+
output.push(buildWorksheetStep("total", definition.id, definition.name, result.value, `${definition.aggregation} = ${result.value}`));
|
|
542
543
|
return output;
|
|
543
544
|
}
|
|
544
545
|
/**
|
|
545
|
-
*
|
|
546
|
+
* Joins a {@link QuantitativeDefinition} and its {@link QuantitativeResult} into
|
|
546
547
|
* a {@link Worksheet} — the rating audit trail.
|
|
547
548
|
*
|
|
548
549
|
* @param definition - The authored quantitative definition
|
|
@@ -552,16 +553,16 @@ function worksheetSteps(definition, result, groups) {
|
|
|
552
553
|
*
|
|
553
554
|
* @example
|
|
554
555
|
* ```ts
|
|
555
|
-
* import {
|
|
556
|
-
* import {
|
|
556
|
+
* import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'
|
|
557
|
+
* import { buildWorksheet } from '@orkestrel/rater'
|
|
557
558
|
*
|
|
558
|
-
* const definition =
|
|
559
|
+
* const definition = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])
|
|
559
560
|
* const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)
|
|
560
|
-
* if (result.reasoning === 'quantitative')
|
|
561
|
+
* if (result.reasoning === 'quantitative') buildWorksheet(definition, result) // the full audit trail
|
|
561
562
|
* ```
|
|
562
563
|
*/
|
|
563
|
-
function
|
|
564
|
-
const groups = definition.groups.map((group) =>
|
|
564
|
+
function buildWorksheet(definition, result, labels) {
|
|
565
|
+
const groups = definition.groups.map((group) => buildWorksheetGroup(group, result.groups, labels));
|
|
565
566
|
return {
|
|
566
567
|
id: definition.id,
|
|
567
568
|
name: definition.name,
|
|
@@ -569,14 +570,14 @@ function resultsWorksheet(definition, result, labels) {
|
|
|
569
570
|
...definition.precision === void 0 ? {} : { precision: definition.precision },
|
|
570
571
|
value: result.value,
|
|
571
572
|
groups,
|
|
572
|
-
steps:
|
|
573
|
+
steps: buildWorksheetSteps(definition, result, groups),
|
|
573
574
|
trace: result.trace,
|
|
574
575
|
errors: result.errors,
|
|
575
576
|
success: result.success
|
|
576
577
|
};
|
|
577
578
|
}
|
|
578
579
|
/**
|
|
579
|
-
*
|
|
580
|
+
* Builds a rated {@link LineResult} from a line's evaluated
|
|
580
581
|
* {@link QuantitativeResult}.
|
|
581
582
|
*
|
|
582
583
|
* @param definition - The authored line definition
|
|
@@ -586,38 +587,37 @@ function resultsWorksheet(definition, result, labels) {
|
|
|
586
587
|
*
|
|
587
588
|
* @example
|
|
588
589
|
* ```ts
|
|
589
|
-
* import { createQuantitativeReasoner, createReason
|
|
590
|
-
* import {
|
|
590
|
+
* import { createQuantitativeDefinition, createQuantitativeReasoner, createReason } from '@orkestrel/reason'
|
|
591
|
+
* import { buildLineDefinition, buildLineResult } from '@orkestrel/rater'
|
|
591
592
|
*
|
|
592
|
-
* const line =
|
|
593
|
+
* const line = buildLineDefinition('base', 'Base', createQuantitativeDefinition('base', 'Base', []))
|
|
593
594
|
* const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, line.rate)
|
|
594
|
-
* if (result.reasoning === 'quantitative')
|
|
595
|
+
* if (result.reasoning === 'quantitative') buildLineResult(line, result) // amount present only on a successful worksheet
|
|
595
596
|
* ```
|
|
596
597
|
*/
|
|
597
|
-
function
|
|
598
|
+
function buildLineResult(definition, result, labels) {
|
|
598
599
|
return {
|
|
599
600
|
id: definition.id,
|
|
600
601
|
name: definition.name,
|
|
601
602
|
...result.success ? { amount: result.value } : {},
|
|
602
|
-
worksheet:
|
|
603
|
-
success: result.success
|
|
603
|
+
worksheet: buildWorksheet(definition.rate, result, labels)
|
|
604
604
|
};
|
|
605
605
|
}
|
|
606
606
|
/**
|
|
607
|
-
*
|
|
607
|
+
* Sums defined line amounts.
|
|
608
608
|
*
|
|
609
609
|
* @param lines - The rated lines
|
|
610
610
|
* @returns The sum of every line's `amount`, or `undefined` when no line has one
|
|
611
611
|
*
|
|
612
612
|
* @example
|
|
613
613
|
* ```ts
|
|
614
|
-
* import {
|
|
615
|
-
* import {
|
|
614
|
+
* import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'
|
|
615
|
+
* import { buildLineDefinition, buildLineResult, sumAmounts } from '@orkestrel/rater'
|
|
616
616
|
*
|
|
617
|
-
* const rate =
|
|
618
|
-
* const line =
|
|
617
|
+
* const rate = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])
|
|
618
|
+
* const line = buildLineDefinition('base', 'Base', rate)
|
|
619
619
|
* const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, rate)
|
|
620
|
-
* if (result.reasoning === 'quantitative') sumAmounts([
|
|
620
|
+
* if (result.reasoning === 'quantitative') sumAmounts([buildLineResult(line, result)]) // 100
|
|
621
621
|
* ```
|
|
622
622
|
*/
|
|
623
623
|
function sumAmounts(lines) {
|
|
@@ -632,17 +632,17 @@ function sumAmounts(lines) {
|
|
|
632
632
|
//#endregion
|
|
633
633
|
//#region src/core/Rater.ts
|
|
634
634
|
/**
|
|
635
|
-
*
|
|
635
|
+
* Orchestrates rating — owns (or receives) the shared quantitative
|
|
636
636
|
* reasoning engine and projects results into the rating domain vocabulary.
|
|
637
637
|
*
|
|
638
638
|
* @remarks
|
|
639
639
|
* When no `engine` is injected, `Rater` builds and OWNS its own
|
|
640
640
|
* quantitative-only engine (`bail: false`) — Rater performs NO evaluation
|
|
641
641
|
* arithmetic of its own; it only orchestrates and projects. The
|
|
642
|
-
* array-of-lines `rate` overload is declared FIRST
|
|
643
|
-
* destroys an OWNED engine, then the
|
|
644
|
-
* engine is never destroyed. Afterwards every
|
|
645
|
-
* {@link RaterError} `'DESTROYED'`.
|
|
642
|
+
* array-of-lines `rate` overload is declared FIRST so a plain line list
|
|
643
|
+
* resolves to that form. `destroy()` destroys an OWNED engine, then the
|
|
644
|
+
* emitter LAST; an INJECTED engine is never destroyed. Afterwards every
|
|
645
|
+
* other method throws {@link RaterError} `'DESTROYED'`.
|
|
646
646
|
*
|
|
647
647
|
* @example
|
|
648
648
|
* ```ts
|
|
@@ -684,10 +684,10 @@ var Rater = class {
|
|
|
684
684
|
rate(input, subject) {
|
|
685
685
|
this.#ensureAlive();
|
|
686
686
|
const lines = this.#normalize(input);
|
|
687
|
-
if (!(0,
|
|
687
|
+
if (!(0, _orkestrel_contract.isRecord)(subject)) throw new RaterError("MISMATCH", "Subject must be a record");
|
|
688
688
|
const results = lines.map((line) => this.#rateLine(line, subject));
|
|
689
689
|
const total = (this.#total ?? sumAmounts)(results);
|
|
690
|
-
const success = results.every((entry) => entry.success);
|
|
690
|
+
const success = results.every((entry) => entry.worksheet.success);
|
|
691
691
|
const result = {
|
|
692
692
|
lines: results,
|
|
693
693
|
...total === void 0 ? {} : { total },
|
|
@@ -708,7 +708,7 @@ var Rater = class {
|
|
|
708
708
|
throw new RaterError("DEFINITION", "Rating input must be an array of line definitions or a rating definition");
|
|
709
709
|
}
|
|
710
710
|
#rateLine(line, subject) {
|
|
711
|
-
return
|
|
711
|
+
return buildLineResult(line, this.#reasonQuantitative(subject, line.rate), this.#labels);
|
|
712
712
|
}
|
|
713
713
|
#reasonQuantitative(subject, definition) {
|
|
714
714
|
const result = this.#engine.reason(subject, definition);
|
|
@@ -736,7 +736,7 @@ var Rater = class {
|
|
|
736
736
|
//#endregion
|
|
737
737
|
//#region src/core/factories.ts
|
|
738
738
|
/**
|
|
739
|
-
*
|
|
739
|
+
* Creates a rating orchestrator.
|
|
740
740
|
*
|
|
741
741
|
* @param options - Optional total handler, labels, injected engine, and emitter hooks
|
|
742
742
|
* @returns A {@link RaterInterface}
|
|
@@ -755,9 +755,17 @@ function createRater(options) {
|
|
|
755
755
|
//#endregion
|
|
756
756
|
exports.Rater = Rater;
|
|
757
757
|
exports.RaterError = RaterError;
|
|
758
|
-
exports.
|
|
758
|
+
exports.buildEvidence = buildEvidence;
|
|
759
|
+
exports.buildEvidenceRows = buildEvidenceRows;
|
|
760
|
+
exports.buildLineDefinition = buildLineDefinition;
|
|
761
|
+
exports.buildLineResult = buildLineResult;
|
|
762
|
+
exports.buildRatingDefinition = buildRatingDefinition;
|
|
763
|
+
exports.buildWorksheet = buildWorksheet;
|
|
764
|
+
exports.buildWorksheetFactor = buildWorksheetFactor;
|
|
765
|
+
exports.buildWorksheetGroup = buildWorksheetGroup;
|
|
766
|
+
exports.buildWorksheetStep = buildWorksheetStep;
|
|
767
|
+
exports.buildWorksheetSteps = buildWorksheetSteps;
|
|
759
768
|
exports.createRater = createRater;
|
|
760
|
-
exports.evidenceCheck = evidenceCheck;
|
|
761
769
|
exports.isEvidence = isEvidence;
|
|
762
770
|
exports.isLineDefinition = isLineDefinition;
|
|
763
771
|
exports.isLineResult = isLineResult;
|
|
@@ -769,14 +777,6 @@ exports.isStep = isStep;
|
|
|
769
777
|
exports.isWorksheet = isWorksheet;
|
|
770
778
|
exports.isWorksheetFactor = isWorksheetFactor;
|
|
771
779
|
exports.isWorksheetGroup = isWorksheetGroup;
|
|
772
|
-
exports.lineDefinition = lineDefinition;
|
|
773
|
-
exports.ratedLine = ratedLine;
|
|
774
|
-
exports.ratingDefinition = ratingDefinition;
|
|
775
|
-
exports.resultsWorksheet = resultsWorksheet;
|
|
776
780
|
exports.sumAmounts = sumAmounts;
|
|
777
|
-
exports.worksheetFactor = worksheetFactor;
|
|
778
|
-
exports.worksheetGroup = worksheetGroup;
|
|
779
|
-
exports.worksheetStep = worksheetStep;
|
|
780
|
-
exports.worksheetSteps = worksheetSteps;
|
|
781
781
|
|
|
782
782
|
//# sourceMappingURL=index.cjs.map
|