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