@orkestrel/rater 0.0.12 → 0.0.14
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 +25 -17
- package/dist/src/core/index.cjs +185 -147
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +343 -293
- package/dist/src/core/index.d.ts +343 -293
- package/dist/src/core/index.js +178 -140
- package/dist/src/core/index.js.map +1 -1
- package/package.json +22 -19
package/dist/src/core/index.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { arrayOf, isArray, isBoolean, isJSONValue, isNumber, isString, literalOf, objectOf, recordOf } from "@orkestrel/contract";
|
|
2
|
-
import { buildErrorResult, createQuantitativeReasoner, createReason, formatField, isAggregation, isComparison, isFieldPath, isQuantitativeDefinition
|
|
1
|
+
import { arrayOf, isArray, isBoolean, isJSONValue, isNumber, isRecord, isString, literalOf, objectOf, recordOf } from "@orkestrel/contract";
|
|
2
|
+
import { buildErrorResult, createQuantitativeReasoner, createReason, formatField, isAggregation, isComparison, isFieldPath, isQuantitativeDefinition } from "@orkestrel/reason";
|
|
3
3
|
import { Emitter } from "@orkestrel/emitter";
|
|
4
4
|
//#region src/core/errors.ts
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Represents a coded programmer error thrown by the rating layer.
|
|
7
7
|
*
|
|
8
8
|
* @remarks
|
|
9
9
|
* `DEFINITION` — the `rate` input failed both the array-of-lines and rating
|
|
10
10
|
* definition validation. `MISMATCH` — a rated subject is not a record.
|
|
11
|
-
* `DESTROYED` — use of a destroyed entity.
|
|
11
|
+
* `DESTROYED` — use of a destroyed entity. `context` carries optional
|
|
12
|
+
* structured detail beside the message.
|
|
12
13
|
*
|
|
13
14
|
* @example
|
|
14
15
|
* ```ts
|
|
@@ -28,10 +29,10 @@ var RaterError = class extends Error {
|
|
|
28
29
|
}
|
|
29
30
|
};
|
|
30
31
|
/**
|
|
31
|
-
*
|
|
32
|
+
* Narrows a caught value to a {@link RaterError}.
|
|
32
33
|
*
|
|
33
34
|
* @param value - The caught value to test
|
|
34
|
-
* @returns
|
|
35
|
+
* @returns True if `value` is a `RaterError`; false otherwise
|
|
35
36
|
*
|
|
36
37
|
* @example
|
|
37
38
|
* ```ts
|
|
@@ -50,10 +51,14 @@ function isRaterError(value) {
|
|
|
50
51
|
//#endregion
|
|
51
52
|
//#region src/core/validators.ts
|
|
52
53
|
/**
|
|
53
|
-
*
|
|
54
|
+
* Determines whether a value is a {@link Stage} literal.
|
|
55
|
+
*
|
|
56
|
+
* @remarks
|
|
57
|
+
* A scalar union has no open or exact axis, so this guard accepts one of the
|
|
58
|
+
* `Stage` literals and leaves nothing unchecked.
|
|
54
59
|
*
|
|
55
60
|
* @param value - The value to test
|
|
56
|
-
* @returns
|
|
61
|
+
* @returns True if `value` is `'factor'`, `'group'`, or `'total'`; false otherwise
|
|
57
62
|
*
|
|
58
63
|
* @example
|
|
59
64
|
* ```ts
|
|
@@ -65,21 +70,24 @@ function isRaterError(value) {
|
|
|
65
70
|
*/
|
|
66
71
|
var isStage = literalOf("factor", "group", "total");
|
|
67
72
|
/**
|
|
68
|
-
*
|
|
73
|
+
* Determines whether a value is an exact {@link LineDefinition} record.
|
|
69
74
|
*
|
|
70
75
|
* @remarks
|
|
71
|
-
*
|
|
72
|
-
*
|
|
76
|
+
* Every `LineDefinition` member is checked, including its quantitative
|
|
77
|
+
* definition. Total guard: adversarial input (cycles, hostile prototypes)
|
|
78
|
+
* returns `false`, never throws. The record shape is exact — this package owns
|
|
79
|
+
* the authored input record, so an extra key fails and nothing is left
|
|
80
|
+
* unchecked.
|
|
73
81
|
*
|
|
74
82
|
* @param value - The value to test
|
|
75
|
-
* @returns
|
|
83
|
+
* @returns True if `value` is a `LineDefinition`; false otherwise
|
|
76
84
|
*
|
|
77
85
|
* @example
|
|
78
86
|
* ```ts
|
|
79
|
-
* import {
|
|
87
|
+
* import { createQuantitativeDefinition } from '@orkestrel/reason'
|
|
80
88
|
* import { isLineDefinition } from '@orkestrel/rater'
|
|
81
89
|
*
|
|
82
|
-
* isLineDefinition({ id: 'base', name: 'Base', rate:
|
|
90
|
+
* isLineDefinition({ id: 'base', name: 'Base', rate: createQuantitativeDefinition('base', 'Base', []) }) // true
|
|
83
91
|
* ```
|
|
84
92
|
*/
|
|
85
93
|
function isLineDefinition(value) {
|
|
@@ -92,14 +100,17 @@ function isLineDefinition(value) {
|
|
|
92
100
|
}, ["description", "metadata"])(value);
|
|
93
101
|
}
|
|
94
102
|
/**
|
|
95
|
-
*
|
|
103
|
+
* Determines whether a value is an exact {@link RatingDefinition} record.
|
|
96
104
|
*
|
|
97
105
|
* @remarks
|
|
98
|
-
*
|
|
99
|
-
*
|
|
106
|
+
* Every `RatingDefinition` member is checked, including each nested line
|
|
107
|
+
* definition. Total guard: adversarial input (cycles, hostile prototypes)
|
|
108
|
+
* returns `false`, never throws. The record shape is exact — this package owns
|
|
109
|
+
* the authored input record, so an extra key fails and nothing is left
|
|
110
|
+
* unchecked.
|
|
100
111
|
*
|
|
101
112
|
* @param value - The value to test
|
|
102
|
-
* @returns
|
|
113
|
+
* @returns True if `value` is a `RatingDefinition`; false otherwise
|
|
103
114
|
*
|
|
104
115
|
* @example
|
|
105
116
|
* ```ts
|
|
@@ -118,16 +129,19 @@ function isRatingDefinition(value) {
|
|
|
118
129
|
}, ["description", "metadata"])(value);
|
|
119
130
|
}
|
|
120
131
|
/**
|
|
121
|
-
*
|
|
132
|
+
* Determines whether a value is an open result-side {@link Evidence} object.
|
|
122
133
|
*
|
|
123
134
|
* @remarks
|
|
135
|
+
* Optional `field` is checked as a `FieldPath`, `label` as a string,
|
|
136
|
+
* `comparison` as a `Comparison`, and `met` as a boolean, each when defined.
|
|
124
137
|
* Unknown members, prototypes, and class instances are admitted. Arrays are
|
|
125
138
|
* refused. `expected` and `actual` are `unknown`, so this guard does not read or
|
|
126
|
-
* check them
|
|
127
|
-
*
|
|
139
|
+
* check them and a borrowed result is never narrowed on them; either member may
|
|
140
|
+
* also be absent because `objectOf` reads declared members rather than
|
|
141
|
+
* requiring or enumerating own keys.
|
|
128
142
|
*
|
|
129
143
|
* @param value - The value to test
|
|
130
|
-
* @returns
|
|
144
|
+
* @returns True if every checked `Evidence` member conforms; false otherwise
|
|
131
145
|
*
|
|
132
146
|
* @example
|
|
133
147
|
* ```ts
|
|
@@ -150,14 +164,17 @@ function isEvidence(value) {
|
|
|
150
164
|
])(value);
|
|
151
165
|
}
|
|
152
166
|
/**
|
|
153
|
-
*
|
|
167
|
+
* Determines whether a value is an open {@link WorksheetFactor} result object.
|
|
154
168
|
*
|
|
155
169
|
* @remarks
|
|
156
|
-
*
|
|
157
|
-
*
|
|
170
|
+
* `id`, the optional authored text, `applied`, an optional plain-number
|
|
171
|
+
* `value`, and each `Evidence` entry are checked. Unknown members, prototypes,
|
|
172
|
+
* and class instances are admitted and left unchecked, because a borrowed
|
|
173
|
+
* result implementation may add them. Arrays are refused. Optional members may
|
|
174
|
+
* be absent or read as `undefined`.
|
|
158
175
|
*
|
|
159
176
|
* @param value - The value to test
|
|
160
|
-
* @returns
|
|
177
|
+
* @returns True if every published worksheet-factor member conforms; false otherwise
|
|
161
178
|
*
|
|
162
179
|
* @example
|
|
163
180
|
* ```ts
|
|
@@ -181,14 +198,17 @@ function isWorksheetFactor(value) {
|
|
|
181
198
|
])(value);
|
|
182
199
|
}
|
|
183
200
|
/**
|
|
184
|
-
*
|
|
201
|
+
* Determines whether a value is an open {@link WorksheetGroup} result object.
|
|
185
202
|
*
|
|
186
203
|
* @remarks
|
|
187
|
-
*
|
|
188
|
-
*
|
|
204
|
+
* `id`, the optional authored text, `applied`, a plain-number `value`, and each
|
|
205
|
+
* `WorksheetFactor` entry are checked. Unknown members, prototypes, and class
|
|
206
|
+
* instances are admitted and left unchecked, because a borrowed result
|
|
207
|
+
* implementation may add them. Arrays are refused. Optional members may be
|
|
208
|
+
* absent or read as `undefined`.
|
|
189
209
|
*
|
|
190
210
|
* @param value - The value to test
|
|
191
|
-
* @returns
|
|
211
|
+
* @returns True if every published worksheet-group member conforms; false otherwise
|
|
192
212
|
*
|
|
193
213
|
* @example
|
|
194
214
|
* ```ts
|
|
@@ -208,14 +228,17 @@ function isWorksheetGroup(value) {
|
|
|
208
228
|
}, ["name", "description"])(value);
|
|
209
229
|
}
|
|
210
230
|
/**
|
|
211
|
-
*
|
|
231
|
+
* Determines whether a value is an open {@link Step} result object.
|
|
212
232
|
*
|
|
213
233
|
* @remarks
|
|
214
|
-
*
|
|
215
|
-
*
|
|
234
|
+
* `stage` is checked through `isStage`, `id`, `name`, and `expression` when
|
|
235
|
+
* defined, and `value` as a plain number. Unknown members, prototypes, and
|
|
236
|
+
* class instances are admitted and left unchecked, because a borrowed result
|
|
237
|
+
* implementation may add them. Arrays are refused. Optional members may be
|
|
238
|
+
* absent or read as `undefined`.
|
|
216
239
|
*
|
|
217
240
|
* @param value - The value to test
|
|
218
|
-
* @returns
|
|
241
|
+
* @returns True if every published worksheet-step member conforms; false otherwise
|
|
219
242
|
*
|
|
220
243
|
* @example
|
|
221
244
|
* ```ts
|
|
@@ -238,14 +261,18 @@ function isStep(value) {
|
|
|
238
261
|
])(value);
|
|
239
262
|
}
|
|
240
263
|
/**
|
|
241
|
-
*
|
|
264
|
+
* Determines whether a value is an open {@link Worksheet} result object.
|
|
242
265
|
*
|
|
243
266
|
* @remarks
|
|
244
|
-
*
|
|
245
|
-
*
|
|
267
|
+
* Identity, `aggregation` through reason's `isAggregation`, an optional
|
|
268
|
+
* plain-number `precision`, a plain-number `value`, the nested groups and steps,
|
|
269
|
+
* `trace`, `errors`, and `success` are checked. Unknown members, prototypes, and
|
|
270
|
+
* class instances are admitted and left unchecked, because a borrowed result
|
|
271
|
+
* implementation may add them. Arrays are refused. Optional members may be
|
|
272
|
+
* absent or read as `undefined`.
|
|
246
273
|
*
|
|
247
274
|
* @param value - The value to test
|
|
248
|
-
* @returns
|
|
275
|
+
* @returns True if every published worksheet member conforms; false otherwise
|
|
249
276
|
*
|
|
250
277
|
* @example
|
|
251
278
|
* ```ts
|
|
@@ -269,21 +296,26 @@ function isWorksheet(value) {
|
|
|
269
296
|
}, ["precision"])(value);
|
|
270
297
|
}
|
|
271
298
|
/**
|
|
272
|
-
*
|
|
299
|
+
* Determines whether a value is an open {@link LineResult} object.
|
|
273
300
|
*
|
|
274
301
|
* @remarks
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
302
|
+
* Identity, an optional plain-number `amount`, and the nested `Worksheet` that
|
|
303
|
+
* carries the line's outcome are checked. Unknown members, prototypes, and
|
|
304
|
+
* class instances are admitted and left unchecked, because a borrowed result
|
|
305
|
+
* implementation may add them, and so is the relationship between `amount` and
|
|
306
|
+
* `worksheet.success`, which the published interface types independently.
|
|
307
|
+
* Arrays are refused. `amount` may be absent or read as `undefined`; when
|
|
308
|
+
* present it stays a plain JavaScript `number` without a finite or range
|
|
309
|
+
* refinement.
|
|
278
310
|
*
|
|
279
311
|
* @param value - The value to test
|
|
280
|
-
* @returns
|
|
312
|
+
* @returns True if every published line-result member conforms; false otherwise
|
|
281
313
|
*
|
|
282
314
|
* @example
|
|
283
315
|
* ```ts
|
|
284
316
|
* import { isLineResult } from '@orkestrel/rater'
|
|
285
317
|
*
|
|
286
|
-
* isLineResult({ id: 'base', name: 'Base', worksheet: { id: 'base', name: 'Base', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: false }
|
|
318
|
+
* isLineResult({ id: 'base', name: 'Base', worksheet: { id: 'base', name: 'Base', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: false } }) // true
|
|
287
319
|
* ```
|
|
288
320
|
*/
|
|
289
321
|
function isLineResult(value) {
|
|
@@ -291,21 +323,23 @@ function isLineResult(value) {
|
|
|
291
323
|
id: isString,
|
|
292
324
|
name: isString,
|
|
293
325
|
amount: isNumber,
|
|
294
|
-
worksheet: isWorksheet
|
|
295
|
-
success: isBoolean
|
|
326
|
+
worksheet: isWorksheet
|
|
296
327
|
}, ["amount"])(value);
|
|
297
328
|
}
|
|
298
329
|
/**
|
|
299
|
-
*
|
|
330
|
+
* Determines whether a value is an open {@link RatingResult} object.
|
|
300
331
|
*
|
|
301
332
|
* @remarks
|
|
302
|
-
* Use this guard for a result returned by a borrowed `RaterInterface`.
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
333
|
+
* Use this guard for a result returned by a borrowed `RaterInterface`. Every
|
|
334
|
+
* nested `LineResult`, an optional plain-number `total`, and a boolean `success`
|
|
335
|
+
* are checked. Unknown members, prototypes, and class instances are admitted and
|
|
336
|
+
* left unchecked, and so are the relationships among totals, lines, and success,
|
|
337
|
+
* because the borrowed interface publishes only their member types. Arrays are
|
|
338
|
+
* refused. `total` may be absent or read as `undefined`; when present it stays a
|
|
339
|
+
* plain JavaScript `number` without a finite or range refinement.
|
|
306
340
|
*
|
|
307
341
|
* @param value - The value to test
|
|
308
|
-
* @returns
|
|
342
|
+
* @returns True if every published rating-result member conforms; false otherwise
|
|
309
343
|
*
|
|
310
344
|
* @example
|
|
311
345
|
* ```ts
|
|
@@ -324,7 +358,9 @@ function isRatingResult(value) {
|
|
|
324
358
|
//#endregion
|
|
325
359
|
//#region src/core/helpers.ts
|
|
326
360
|
/**
|
|
327
|
-
*
|
|
361
|
+
* Builds a fresh {@link LineDefinition} from a line id, a display name, and the
|
|
362
|
+
* line's quantitative rating definition, with `overrides` merged over those
|
|
363
|
+
* defaults.
|
|
328
364
|
*
|
|
329
365
|
* @param id - The line id
|
|
330
366
|
* @param name - The display name
|
|
@@ -334,13 +370,13 @@ function isRatingResult(value) {
|
|
|
334
370
|
*
|
|
335
371
|
* @example
|
|
336
372
|
* ```ts
|
|
337
|
-
* import {
|
|
338
|
-
* import {
|
|
373
|
+
* import { createQuantitativeDefinition } from '@orkestrel/reason'
|
|
374
|
+
* import { buildLineDefinition } from '@orkestrel/rater'
|
|
339
375
|
*
|
|
340
|
-
*
|
|
376
|
+
* buildLineDefinition('base', 'Base Amount', createQuantitativeDefinition('base', 'Base', []))
|
|
341
377
|
* ```
|
|
342
378
|
*/
|
|
343
|
-
function
|
|
379
|
+
function buildLineDefinition(id, name, rate, overrides) {
|
|
344
380
|
return {
|
|
345
381
|
id,
|
|
346
382
|
name,
|
|
@@ -349,7 +385,8 @@ function lineDefinition(id, name, rate, overrides) {
|
|
|
349
385
|
};
|
|
350
386
|
}
|
|
351
387
|
/**
|
|
352
|
-
*
|
|
388
|
+
* Builds a fresh {@link RatingDefinition} from a rating id, a display name, and
|
|
389
|
+
* the rating's ordered lines, with `overrides` merged over those defaults.
|
|
353
390
|
*
|
|
354
391
|
* @param id - The rating id
|
|
355
392
|
* @param name - The display name
|
|
@@ -359,15 +396,15 @@ function lineDefinition(id, name, rate, overrides) {
|
|
|
359
396
|
*
|
|
360
397
|
* @example
|
|
361
398
|
* ```ts
|
|
362
|
-
* import {
|
|
363
|
-
* import {
|
|
399
|
+
* import { buildLineDefinition, buildRatingDefinition } from '@orkestrel/rater'
|
|
400
|
+
* import { createQuantitativeDefinition } from '@orkestrel/reason'
|
|
364
401
|
*
|
|
365
|
-
*
|
|
366
|
-
*
|
|
402
|
+
* buildRatingDefinition('r1', 'Rating', [
|
|
403
|
+
* buildLineDefinition('base', 'Base Amount', createQuantitativeDefinition('base', 'Base', [])),
|
|
367
404
|
* ])
|
|
368
405
|
* ```
|
|
369
406
|
*/
|
|
370
|
-
function
|
|
407
|
+
function buildRatingDefinition(id, name, lines, overrides) {
|
|
371
408
|
return {
|
|
372
409
|
id,
|
|
373
410
|
name,
|
|
@@ -376,24 +413,25 @@ function ratingDefinition(id, name, lines, overrides) {
|
|
|
376
413
|
};
|
|
377
414
|
}
|
|
378
415
|
/**
|
|
379
|
-
*
|
|
416
|
+
* Builds an {@link Evidence} row from an evaluated {@link Check}.
|
|
380
417
|
*
|
|
381
418
|
* @param check - The authored check
|
|
382
419
|
* @param actual - The resolved subject value
|
|
383
|
-
* @param met -
|
|
420
|
+
* @param met - If `true`, the check was met; if `false`, it was not; absent when the check
|
|
421
|
+
* was not yet evaluated
|
|
384
422
|
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
385
423
|
* @returns A fresh evidence row
|
|
386
424
|
*
|
|
387
425
|
* @example
|
|
388
426
|
* ```ts
|
|
389
|
-
* import {
|
|
390
|
-
* import {
|
|
427
|
+
* import { createCheck } from '@orkestrel/reason'
|
|
428
|
+
* import { buildEvidence } from '@orkestrel/rater'
|
|
391
429
|
*
|
|
392
|
-
* const entry =
|
|
393
|
-
*
|
|
430
|
+
* const entry = createCheck('age', 'above', 18)
|
|
431
|
+
* buildEvidence(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
|
|
394
432
|
* ```
|
|
395
433
|
*/
|
|
396
|
-
function
|
|
434
|
+
function buildEvidence(check, actual, met, labels) {
|
|
397
435
|
const field = formatField(check.field);
|
|
398
436
|
return {
|
|
399
437
|
field: check.field,
|
|
@@ -405,8 +443,8 @@ function evidenceCheck(check, actual, met, labels) {
|
|
|
405
443
|
};
|
|
406
444
|
}
|
|
407
445
|
/**
|
|
408
|
-
*
|
|
409
|
-
*
|
|
446
|
+
* Builds one evidence row per authored check of a quantitative factor, joined
|
|
447
|
+
* to that check's evaluated result.
|
|
410
448
|
*
|
|
411
449
|
* @param checks - The factor's authored checks
|
|
412
450
|
* @param results - The corresponding {@link CheckResult}s, in the same order
|
|
@@ -415,21 +453,21 @@ function evidenceCheck(check, actual, met, labels) {
|
|
|
415
453
|
*
|
|
416
454
|
* @example
|
|
417
455
|
* ```ts
|
|
418
|
-
* import {
|
|
419
|
-
* import {
|
|
456
|
+
* import { createCheck } from '@orkestrel/reason'
|
|
457
|
+
* import { buildEvidenceRows } from '@orkestrel/rater'
|
|
420
458
|
*
|
|
421
|
-
* const checks = [
|
|
422
|
-
*
|
|
459
|
+
* const checks = [createCheck('age', 'above', 18)]
|
|
460
|
+
* buildEvidenceRows(checks, [{ field: 'age', met: true, actual: 25 }])
|
|
423
461
|
* ```
|
|
424
462
|
*/
|
|
425
|
-
function
|
|
463
|
+
function buildEvidenceRows(checks, results, labels) {
|
|
426
464
|
return (checks ?? []).map((check, index) => {
|
|
427
465
|
const result = results?.[index];
|
|
428
|
-
return
|
|
466
|
+
return buildEvidence(check, result?.actual, result?.met, labels);
|
|
429
467
|
});
|
|
430
468
|
}
|
|
431
469
|
/**
|
|
432
|
-
*
|
|
470
|
+
* Joins one authored quantitative factor to its evaluated {@link FactorResult}.
|
|
433
471
|
*
|
|
434
472
|
* @param definition - The authored factor
|
|
435
473
|
* @param results - The group's factor results
|
|
@@ -438,14 +476,14 @@ function checkEvidence(checks, results, labels) {
|
|
|
438
476
|
*
|
|
439
477
|
* @example
|
|
440
478
|
* ```ts
|
|
441
|
-
* import {
|
|
442
|
-
* import {
|
|
479
|
+
* import { createFieldFactor } from '@orkestrel/reason'
|
|
480
|
+
* import { buildWorksheetFactor } from '@orkestrel/rater'
|
|
443
481
|
*
|
|
444
|
-
* const factor =
|
|
445
|
-
*
|
|
482
|
+
* const factor = createFieldFactor('seats', 'seats')
|
|
483
|
+
* buildWorksheetFactor(factor, [{ id: 'seats', applied: true, value: 10 }])
|
|
446
484
|
* ```
|
|
447
485
|
*/
|
|
448
|
-
function
|
|
486
|
+
function buildWorksheetFactor(definition, results, labels) {
|
|
449
487
|
const result = results.find((entry) => entry.id === definition.id);
|
|
450
488
|
return {
|
|
451
489
|
id: definition.id,
|
|
@@ -453,11 +491,11 @@ function worksheetFactor(definition, results, labels) {
|
|
|
453
491
|
...definition.description === void 0 ? {} : { description: definition.description },
|
|
454
492
|
applied: result?.applied ?? false,
|
|
455
493
|
...result?.value === void 0 ? {} : { value: result.value },
|
|
456
|
-
evidence:
|
|
494
|
+
evidence: buildEvidenceRows(definition.checks, result?.checks, labels)
|
|
457
495
|
};
|
|
458
496
|
}
|
|
459
497
|
/**
|
|
460
|
-
*
|
|
498
|
+
* Joins one authored quantitative group to its evaluated {@link GroupResult}.
|
|
461
499
|
*
|
|
462
500
|
* @param definition - The authored group
|
|
463
501
|
* @param results - The definition's group results
|
|
@@ -466,14 +504,14 @@ function worksheetFactor(definition, results, labels) {
|
|
|
466
504
|
*
|
|
467
505
|
* @example
|
|
468
506
|
* ```ts
|
|
469
|
-
* import {
|
|
470
|
-
* import {
|
|
507
|
+
* import { createFactorGroup, createStaticFactor } from '@orkestrel/reason'
|
|
508
|
+
* import { buildWorksheetGroup } from '@orkestrel/rater'
|
|
471
509
|
*
|
|
472
|
-
* const group =
|
|
473
|
-
*
|
|
510
|
+
* const group = createFactorGroup('charge', 'sum', [createStaticFactor('base', 100)])
|
|
511
|
+
* buildWorksheetGroup(group, [{ id: 'charge', applied: true, value: 100, factors: [] }])
|
|
474
512
|
* ```
|
|
475
513
|
*/
|
|
476
|
-
function
|
|
514
|
+
function buildWorksheetGroup(definition, results, labels) {
|
|
477
515
|
const result = results.find((entry) => entry.id === definition.id);
|
|
478
516
|
return {
|
|
479
517
|
id: definition.id,
|
|
@@ -481,11 +519,11 @@ function worksheetGroup(definition, results, labels) {
|
|
|
481
519
|
...definition.description === void 0 ? {} : { description: definition.description },
|
|
482
520
|
applied: result?.applied ?? false,
|
|
483
521
|
value: result?.value ?? 0,
|
|
484
|
-
factors: definition.factors.map((factor) =>
|
|
522
|
+
factors: definition.factors.map((factor) => buildWorksheetFactor(factor, result?.factors ?? [], labels))
|
|
485
523
|
};
|
|
486
524
|
}
|
|
487
525
|
/**
|
|
488
|
-
*
|
|
526
|
+
* Builds one display-neutral {@link Step} row.
|
|
489
527
|
*
|
|
490
528
|
* @param stage - The step's stage
|
|
491
529
|
* @param id - The stage's authored id, when it has one
|
|
@@ -496,13 +534,13 @@ function worksheetGroup(definition, results, labels) {
|
|
|
496
534
|
*
|
|
497
535
|
* @example
|
|
498
536
|
* ```ts
|
|
499
|
-
* import {
|
|
537
|
+
* import { buildWorksheetStep } from '@orkestrel/rater'
|
|
500
538
|
*
|
|
501
|
-
*
|
|
539
|
+
* buildWorksheetStep('total', 'quote', 'Quote', 110, 'sum = 110')
|
|
502
540
|
* // { stage: 'total', id: 'quote', name: 'Quote', value: 110, expression: 'sum = 110' }
|
|
503
541
|
* ```
|
|
504
542
|
*/
|
|
505
|
-
function
|
|
543
|
+
function buildWorksheetStep(stage, id, name, value, expression) {
|
|
506
544
|
return {
|
|
507
545
|
stage,
|
|
508
546
|
...id === void 0 ? {} : { id },
|
|
@@ -512,7 +550,7 @@ function worksheetStep(stage, id, name, value, expression) {
|
|
|
512
550
|
};
|
|
513
551
|
}
|
|
514
552
|
/**
|
|
515
|
-
*
|
|
553
|
+
* Builds the ordered {@link Step} rows for a resolved {@link Worksheet}.
|
|
516
554
|
*
|
|
517
555
|
* @param definition - The authored quantitative definition
|
|
518
556
|
* @param result - The evaluated quantitative result
|
|
@@ -521,27 +559,27 @@ function worksheetStep(stage, id, name, value, expression) {
|
|
|
521
559
|
*
|
|
522
560
|
* @example
|
|
523
561
|
* ```ts
|
|
524
|
-
* import {
|
|
525
|
-
* import {
|
|
562
|
+
* import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'
|
|
563
|
+
* import { buildWorksheetGroup, buildWorksheetSteps } from '@orkestrel/rater'
|
|
526
564
|
*
|
|
527
|
-
* const definition =
|
|
565
|
+
* const definition = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])
|
|
528
566
|
* const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)
|
|
529
567
|
* if (result.reasoning === 'quantitative') {
|
|
530
|
-
*
|
|
568
|
+
* buildWorksheetSteps(definition, result, definition.groups.map((group) => buildWorksheetGroup(group, result.groups)))
|
|
531
569
|
* }
|
|
532
570
|
* ```
|
|
533
571
|
*/
|
|
534
|
-
function
|
|
572
|
+
function buildWorksheetSteps(definition, result, groups) {
|
|
535
573
|
const output = [];
|
|
536
574
|
for (const group of groups) {
|
|
537
|
-
for (const factor of group.factors) if (factor.applied && factor.value !== void 0) output.push(
|
|
538
|
-
output.push(
|
|
575
|
+
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}`));
|
|
576
|
+
output.push(buildWorksheetStep("group", group.id, group.name, group.value, `${group.id} = ${group.value}`));
|
|
539
577
|
}
|
|
540
|
-
output.push(
|
|
578
|
+
output.push(buildWorksheetStep("total", definition.id, definition.name, result.value, `${definition.aggregation} = ${result.value}`));
|
|
541
579
|
return output;
|
|
542
580
|
}
|
|
543
581
|
/**
|
|
544
|
-
*
|
|
582
|
+
* Joins a {@link QuantitativeDefinition} and its {@link QuantitativeResult} into
|
|
545
583
|
* a {@link Worksheet} — the rating audit trail.
|
|
546
584
|
*
|
|
547
585
|
* @param definition - The authored quantitative definition
|
|
@@ -551,16 +589,16 @@ function worksheetSteps(definition, result, groups) {
|
|
|
551
589
|
*
|
|
552
590
|
* @example
|
|
553
591
|
* ```ts
|
|
554
|
-
* import {
|
|
555
|
-
* import {
|
|
592
|
+
* import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'
|
|
593
|
+
* import { buildWorksheet } from '@orkestrel/rater'
|
|
556
594
|
*
|
|
557
|
-
* const definition =
|
|
595
|
+
* const definition = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])
|
|
558
596
|
* const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)
|
|
559
|
-
* if (result.reasoning === 'quantitative')
|
|
597
|
+
* if (result.reasoning === 'quantitative') buildWorksheet(definition, result) // the full audit trail
|
|
560
598
|
* ```
|
|
561
599
|
*/
|
|
562
|
-
function
|
|
563
|
-
const groups = definition.groups.map((group) =>
|
|
600
|
+
function buildWorksheet(definition, result, labels) {
|
|
601
|
+
const groups = definition.groups.map((group) => buildWorksheetGroup(group, result.groups, labels));
|
|
564
602
|
return {
|
|
565
603
|
id: definition.id,
|
|
566
604
|
name: definition.name,
|
|
@@ -568,14 +606,14 @@ function resultsWorksheet(definition, result, labels) {
|
|
|
568
606
|
...definition.precision === void 0 ? {} : { precision: definition.precision },
|
|
569
607
|
value: result.value,
|
|
570
608
|
groups,
|
|
571
|
-
steps:
|
|
609
|
+
steps: buildWorksheetSteps(definition, result, groups),
|
|
572
610
|
trace: result.trace,
|
|
573
611
|
errors: result.errors,
|
|
574
612
|
success: result.success
|
|
575
613
|
};
|
|
576
614
|
}
|
|
577
615
|
/**
|
|
578
|
-
*
|
|
616
|
+
* Builds a rated {@link LineResult} from a line's evaluated
|
|
579
617
|
* {@link QuantitativeResult}.
|
|
580
618
|
*
|
|
581
619
|
* @param definition - The authored line definition
|
|
@@ -585,38 +623,37 @@ function resultsWorksheet(definition, result, labels) {
|
|
|
585
623
|
*
|
|
586
624
|
* @example
|
|
587
625
|
* ```ts
|
|
588
|
-
* import { createQuantitativeReasoner, createReason
|
|
589
|
-
* import {
|
|
626
|
+
* import { createQuantitativeDefinition, createQuantitativeReasoner, createReason } from '@orkestrel/reason'
|
|
627
|
+
* import { buildLineDefinition, buildLineResult } from '@orkestrel/rater'
|
|
590
628
|
*
|
|
591
|
-
* const line =
|
|
629
|
+
* const line = buildLineDefinition('base', 'Base', createQuantitativeDefinition('base', 'Base', []))
|
|
592
630
|
* const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, line.rate)
|
|
593
|
-
* if (result.reasoning === 'quantitative')
|
|
631
|
+
* if (result.reasoning === 'quantitative') buildLineResult(line, result) // amount present only on a successful worksheet
|
|
594
632
|
* ```
|
|
595
633
|
*/
|
|
596
|
-
function
|
|
634
|
+
function buildLineResult(definition, result, labels) {
|
|
597
635
|
return {
|
|
598
636
|
id: definition.id,
|
|
599
637
|
name: definition.name,
|
|
600
638
|
...result.success ? { amount: result.value } : {},
|
|
601
|
-
worksheet:
|
|
602
|
-
success: result.success
|
|
639
|
+
worksheet: buildWorksheet(definition.rate, result, labels)
|
|
603
640
|
};
|
|
604
641
|
}
|
|
605
642
|
/**
|
|
606
|
-
*
|
|
643
|
+
* Sums defined line amounts.
|
|
607
644
|
*
|
|
608
645
|
* @param lines - The rated lines
|
|
609
646
|
* @returns The sum of every line's `amount`, or `undefined` when no line has one
|
|
610
647
|
*
|
|
611
648
|
* @example
|
|
612
649
|
* ```ts
|
|
613
|
-
* import {
|
|
614
|
-
* import {
|
|
650
|
+
* import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'
|
|
651
|
+
* import { buildLineDefinition, buildLineResult, sumAmounts } from '@orkestrel/rater'
|
|
615
652
|
*
|
|
616
|
-
* const rate =
|
|
617
|
-
* const line =
|
|
653
|
+
* const rate = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])
|
|
654
|
+
* const line = buildLineDefinition('base', 'Base', rate)
|
|
618
655
|
* const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, rate)
|
|
619
|
-
* if (result.reasoning === 'quantitative') sumAmounts([
|
|
656
|
+
* if (result.reasoning === 'quantitative') sumAmounts([buildLineResult(line, result)]) // 100
|
|
620
657
|
* ```
|
|
621
658
|
*/
|
|
622
659
|
function sumAmounts(lines) {
|
|
@@ -631,17 +668,17 @@ function sumAmounts(lines) {
|
|
|
631
668
|
//#endregion
|
|
632
669
|
//#region src/core/Rater.ts
|
|
633
670
|
/**
|
|
634
|
-
*
|
|
671
|
+
* Orchestrates rating — owns (or receives) the shared quantitative
|
|
635
672
|
* reasoning engine and projects results into the rating domain vocabulary.
|
|
636
673
|
*
|
|
637
674
|
* @remarks
|
|
638
|
-
* When no `engine` is injected, `Rater` builds and
|
|
639
|
-
* quantitative-only engine (`bail: false`) — Rater performs
|
|
675
|
+
* When no `engine` is injected, `Rater` builds and owns its own
|
|
676
|
+
* quantitative-only engine (`bail: false`) — Rater performs no evaluation
|
|
640
677
|
* arithmetic of its own; it only orchestrates and projects. The
|
|
641
|
-
* array-of-lines `rate` overload is declared
|
|
642
|
-
* destroys an
|
|
643
|
-
* engine is never destroyed. Afterwards every
|
|
644
|
-
* {@link RaterError} `'DESTROYED'`.
|
|
678
|
+
* array-of-lines `rate` overload is declared first so a plain line list
|
|
679
|
+
* resolves to that form. `destroy()` destroys an owned engine, then the
|
|
680
|
+
* emitter last; an injected engine is never destroyed. Afterwards every
|
|
681
|
+
* other method throws {@link RaterError} `'DESTROYED'`.
|
|
645
682
|
*
|
|
646
683
|
* @example
|
|
647
684
|
* ```ts
|
|
@@ -683,10 +720,10 @@ var Rater = class {
|
|
|
683
720
|
rate(input, subject) {
|
|
684
721
|
this.#ensureAlive();
|
|
685
722
|
const lines = this.#normalize(input);
|
|
686
|
-
if (!
|
|
723
|
+
if (!isRecord(subject)) throw new RaterError("MISMATCH", "Subject must be a record");
|
|
687
724
|
const results = lines.map((line) => this.#rateLine(line, subject));
|
|
688
725
|
const total = (this.#total ?? sumAmounts)(results);
|
|
689
|
-
const success = results.every((entry) => entry.success);
|
|
726
|
+
const success = results.every((entry) => entry.worksheet.success);
|
|
690
727
|
const result = {
|
|
691
728
|
lines: results,
|
|
692
729
|
...total === void 0 ? {} : { total },
|
|
@@ -707,7 +744,7 @@ var Rater = class {
|
|
|
707
744
|
throw new RaterError("DEFINITION", "Rating input must be an array of line definitions or a rating definition");
|
|
708
745
|
}
|
|
709
746
|
#rateLine(line, subject) {
|
|
710
|
-
return
|
|
747
|
+
return buildLineResult(line, this.#reasonQuantitative(subject, line.rate), this.#labels);
|
|
711
748
|
}
|
|
712
749
|
#reasonQuantitative(subject, definition) {
|
|
713
750
|
const result = this.#engine.reason(subject, definition);
|
|
@@ -735,12 +772,13 @@ var Rater = class {
|
|
|
735
772
|
//#endregion
|
|
736
773
|
//#region src/core/factories.ts
|
|
737
774
|
/**
|
|
738
|
-
*
|
|
775
|
+
* Creates a rating orchestrator over the shared quantitative engine, seeded
|
|
776
|
+
* from {@link RaterOptions} and returning a {@link RaterInterface}.
|
|
739
777
|
*
|
|
740
778
|
* @param options - Optional total handler, labels, injected engine, and emitter hooks
|
|
741
779
|
* @returns A {@link RaterInterface}
|
|
742
780
|
*
|
|
743
|
-
* @example
|
|
781
|
+
* @example Create a rater
|
|
744
782
|
* ```ts
|
|
745
783
|
* import { createRater } from '@orkestrel/rater'
|
|
746
784
|
*
|
|
@@ -752,6 +790,6 @@ function createRater(options) {
|
|
|
752
790
|
return new Rater(options);
|
|
753
791
|
}
|
|
754
792
|
//#endregion
|
|
755
|
-
export { Rater, RaterError,
|
|
793
|
+
export { Rater, RaterError, buildEvidence, buildEvidenceRows, buildLineDefinition, buildLineResult, buildRatingDefinition, buildWorksheet, buildWorksheetFactor, buildWorksheetGroup, buildWorksheetStep, buildWorksheetSteps, createRater, isEvidence, isLineDefinition, isLineResult, isRaterError, isRatingDefinition, isRatingResult, isStage, isStep, isWorksheet, isWorksheetFactor, isWorksheetGroup, sumAmounts };
|
|
756
794
|
|
|
757
795
|
//# sourceMappingURL=index.js.map
|