@orkestrel/rater 0.0.13 → 0.0.15
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 +13 -9
- package/dist/src/core/index.cjs +71 -33
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +100 -53
- package/dist/src/core/index.d.ts +100 -53
- package/dist/src/core/index.js +72 -34
- package/dist/src/core/index.js.map +1 -1
- package/package.json +15 -16
package/README.md
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
# @orkestrel/rater
|
|
2
2
|
|
|
3
|
-
A typed
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
`
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
> A typed quantitative rating layer over `@orkestrel/reason`'s shared engine: authored
|
|
4
|
+
> lines, each a plain reason `QuantitativeDefinition` joined to display metadata, rated
|
|
5
|
+
> against one subject to produce a `LineResult` per line — an `amount` and its
|
|
6
|
+
> `Worksheet` audit trail — and one `RatingResult` carrying every line's outcome and a
|
|
7
|
+
> derived `total`.
|
|
8
|
+
|
|
9
|
+
Create a rater with the `createRater` function, hand it the lines a subject is rated
|
|
10
|
+
against, and read the `LineResult` rows and the `total` it derives. Inject a
|
|
11
|
+
[`@orkestrel/reason`](https://github.com/orkestrel/reason) `ReasonInterface` where the
|
|
12
|
+
rating shares an engine with the rest of your reasoning, and call `destroy()` when the
|
|
13
|
+
rater's work is done. Environment-agnostic — no I/O, no browser or server assumptions.
|
|
14
|
+
Part of the `@orkestrel` line.
|
|
11
15
|
|
|
12
16
|
## Install
|
|
13
17
|
|
|
@@ -50,7 +54,7 @@ rater.destroy()
|
|
|
50
54
|
```
|
|
51
55
|
|
|
52
56
|
`rate` accepts a plain `LineDefinition[]` or a full `RatingDefinition` plus one
|
|
53
|
-
subject
|
|
57
|
+
subject, and each overload rates that single subject. Every `rate` call fires once
|
|
54
58
|
through `rater.emitter` (`rate`).
|
|
55
59
|
|
|
56
60
|
## Guide
|
package/dist/src/core/index.cjs
CHANGED
|
@@ -9,7 +9,8 @@ let _orkestrel_emitter = require("@orkestrel/emitter");
|
|
|
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
|
|
@@ -46,13 +47,17 @@ var RaterError = class extends Error {
|
|
|
46
47
|
* ```
|
|
47
48
|
*/
|
|
48
49
|
function isRaterError(value) {
|
|
49
|
-
return value
|
|
50
|
+
return (0, _orkestrel_contract.isInstance)(value, RaterError);
|
|
50
51
|
}
|
|
51
52
|
//#endregion
|
|
52
53
|
//#region src/core/validators.ts
|
|
53
54
|
/**
|
|
54
55
|
* Determines whether a value is a {@link Stage} literal.
|
|
55
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.
|
|
60
|
+
*
|
|
56
61
|
* @param value - The value to test
|
|
57
62
|
* @returns True if `value` is `'factor'`, `'group'`, or `'total'`; false otherwise
|
|
58
63
|
*
|
|
@@ -69,8 +74,11 @@ var isStage = (0, _orkestrel_contract.literalOf)("factor", "group", "total");
|
|
|
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
84
|
* @returns True if `value` is a `LineDefinition`; false otherwise
|
|
@@ -96,8 +104,11 @@ function isLineDefinition(value) {
|
|
|
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
114
|
* @returns True if `value` is a `RatingDefinition`; false otherwise
|
|
@@ -122,10 +133,13 @@ function isRatingDefinition(value) {
|
|
|
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
145
|
* @returns True if every checked `Evidence` member conforms; false otherwise
|
|
@@ -154,8 +168,11 @@ function isEvidence(value) {
|
|
|
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
178
|
* @returns True if every published worksheet-factor member conforms; false otherwise
|
|
@@ -185,8 +202,11 @@ function isWorksheetFactor(value) {
|
|
|
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
212
|
* @returns True if every published worksheet-group member conforms; false otherwise
|
|
@@ -212,8 +232,11 @@ function isWorksheetGroup(value) {
|
|
|
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
242
|
* @returns True if every published worksheet-step member conforms; false otherwise
|
|
@@ -242,8 +265,12 @@ function isStep(value) {
|
|
|
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
276
|
* @returns True if every published worksheet member conforms; false otherwise
|
|
@@ -273,10 +300,14 @@ function isWorksheet(value) {
|
|
|
273
300
|
* Determines whether a value is an open {@link LineResult} object.
|
|
274
301
|
*
|
|
275
302
|
* @remarks
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
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.
|
|
280
311
|
*
|
|
281
312
|
* @param value - The value to test
|
|
282
313
|
* @returns True if every published line-result member conforms; false otherwise
|
|
@@ -300,10 +331,13 @@ function isLineResult(value) {
|
|
|
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
343
|
* @returns True if every published rating-result member conforms; false otherwise
|
|
@@ -325,7 +359,9 @@ function isRatingResult(value) {
|
|
|
325
359
|
//#endregion
|
|
326
360
|
//#region src/core/helpers.ts
|
|
327
361
|
/**
|
|
328
|
-
* Builds a {@link LineDefinition}
|
|
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
|
|
@@ -350,7 +386,8 @@ function buildLineDefinition(id, name, rate, overrides) {
|
|
|
350
386
|
};
|
|
351
387
|
}
|
|
352
388
|
/**
|
|
353
|
-
* Builds a {@link RatingDefinition}
|
|
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
|
|
@@ -636,12 +673,12 @@ function sumAmounts(lines) {
|
|
|
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
|
-
* resolves to that form. `destroy()` destroys an
|
|
644
|
-
* emitter
|
|
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
|
|
645
682
|
* other method throws {@link RaterError} `'DESTROYED'`.
|
|
646
683
|
*
|
|
647
684
|
* @example
|
|
@@ -736,12 +773,13 @@ var Rater = class {
|
|
|
736
773
|
//#endregion
|
|
737
774
|
//#region src/core/factories.ts
|
|
738
775
|
/**
|
|
739
|
-
* Creates a rating orchestrator
|
|
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
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["#emitter","#engine","#owned","#total","#labels","#ensureAlive","#normalize","#rateLine","#destroyed","#reasonQuantitative"],"sources":["../../../src/core/errors.ts","../../../src/core/validators.ts","../../../src/core/helpers.ts","../../../src/core/Rater.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { RaterErrorCode } from './types.js'\n\n/**\n * Represents a coded programmer error thrown by the rating layer.\n *\n * @remarks\n * `DEFINITION` — the `rate` input failed both the array-of-lines and rating\n * definition validation. `MISMATCH` — a rated subject is not a record.\n * `DESTROYED` — use of a destroyed entity.\n *\n * @example\n * ```ts\n * import { RaterError } from '@orkestrel/rater'\n *\n * throw new RaterError('MISMATCH', 'Subject must be a record')\n * ```\n */\nexport class RaterError extends Error {\n\treadonly code: RaterErrorCode\n\treadonly context?: Readonly<Record<string, unknown>>\n\n\tconstructor(code: RaterErrorCode, message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message)\n\t\tthis.name = 'RaterError'\n\t\tthis.code = code\n\t\tif (context !== undefined) this.context = context\n\t}\n}\n\n/**\n * Narrows a caught value to a {@link RaterError}.\n *\n * @param value - The caught value to test\n * @returns True if `value` is a `RaterError`; false otherwise\n *\n * @example\n * ```ts\n * import { isRaterError, RaterError } from '@orkestrel/rater'\n *\n * try {\n * \tthrow new RaterError('DESTROYED', 'Rater has been destroyed')\n * } catch (error) {\n * \tif (isRaterError(error)) error.code // 'DESTROYED'\n * }\n * ```\n */\nexport function isRaterError(value: unknown): value is RaterError {\n\treturn value instanceof RaterError\n}\n","import type { Guard } from '@orkestrel/contract'\nimport type {\n\tEvidence,\n\tLineDefinition,\n\tLineResult,\n\tRatingDefinition,\n\tRatingResult,\n\tStage,\n\tStep,\n\tWorksheet,\n\tWorksheetFactor,\n\tWorksheetGroup,\n} from './types.js'\nimport {\n\tarrayOf,\n\tisBoolean,\n\tisJSONValue,\n\tisNumber,\n\tisString,\n\tliteralOf,\n\tobjectOf,\n\trecordOf,\n} from '@orkestrel/contract'\nimport {\n\tisAggregation,\n\tisComparison,\n\tisFieldPath,\n\tisQuantitativeDefinition,\n} from '@orkestrel/reason'\n\n/**\n * Determines whether a value is a {@link Stage} literal.\n *\n * @param value - The value to test\n * @returns True if `value` is `'factor'`, `'group'`, or `'total'`; false otherwise\n *\n * @example\n * ```ts\n * import { isStage } from '@orkestrel/rater'\n *\n * isStage('group') // true\n * isStage('step') // false\n * ```\n */\nexport const isStage: Guard<Stage> = literalOf('factor', 'group', 'total')\n\n/**\n * Determines whether a value is an exact {@link LineDefinition} record.\n *\n * @remarks\n * Total guard: adversarial input (cycles, hostile prototypes) returns `false`,\n * never throws. The record shape is EXACT — an extra key fails.\n *\n * @param value - The value to test\n * @returns True if `value` is a `LineDefinition`; false otherwise\n *\n * @example\n * ```ts\n * import { createQuantitativeDefinition } from '@orkestrel/reason'\n * import { isLineDefinition } from '@orkestrel/rater'\n *\n * isLineDefinition({ id: 'base', name: 'Base', rate: createQuantitativeDefinition('base', 'Base', []) }) // true\n * ```\n */\nexport function isLineDefinition(value: unknown): value is LineDefinition {\n\treturn recordOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\trate: isQuantitativeDefinition,\n\t\t\tmetadata: isJSONValue,\n\t\t},\n\t\t['description', 'metadata'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an exact {@link RatingDefinition} record.\n *\n * @remarks\n * Total guard: adversarial input (cycles, hostile prototypes) returns `false`,\n * never throws. The record shape is EXACT — an extra key fails.\n *\n * @param value - The value to test\n * @returns True if `value` is a `RatingDefinition`; false otherwise\n *\n * @example\n * ```ts\n * import { isRatingDefinition } from '@orkestrel/rater'\n *\n * isRatingDefinition({ id: 'r1', name: 'Rating', lines: [] }) // true\n * ```\n */\nexport function isRatingDefinition(value: unknown): value is RatingDefinition {\n\treturn recordOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\tlines: arrayOf(isLineDefinition),\n\t\t\tmetadata: isJSONValue,\n\t\t},\n\t\t['description', 'metadata'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open result-side {@link Evidence} object.\n *\n * @remarks\n * Unknown members, prototypes, and class instances are admitted. Arrays are\n * refused. `expected` and `actual` are `unknown`, so this guard does not read or\n * check them; either member may also be absent because `objectOf` reads declared\n * members rather than requiring or enumerating own keys.\n *\n * @param value - The value to test\n * @returns True if every checked `Evidence` member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isEvidence } from '@orkestrel/rater'\n *\n * isEvidence({ field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }) // true\n * ```\n */\nexport function isEvidence(value: unknown): value is Evidence {\n\treturn objectOf(\n\t\t{\n\t\t\tfield: isFieldPath,\n\t\t\tlabel: isString,\n\t\t\tcomparison: isComparison,\n\t\t\tmet: isBoolean,\n\t\t},\n\t\t['field', 'label', 'comparison', 'met'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link WorksheetFactor} result object.\n *\n * @remarks\n * Unknown members, prototypes, and class instances are admitted. Arrays are\n * refused. Optional members may be absent or read as `undefined`.\n *\n * @param value - The value to test\n * @returns True if every published worksheet-factor member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isWorksheetFactor } from '@orkestrel/rater'\n *\n * isWorksheetFactor({ id: 'base', applied: true, value: 100, evidence: [] }) // true\n * ```\n */\nexport function isWorksheetFactor(value: unknown): value is WorksheetFactor {\n\treturn objectOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\tapplied: isBoolean,\n\t\t\tvalue: isNumber,\n\t\t\tevidence: arrayOf(isEvidence),\n\t\t},\n\t\t['name', 'description', 'value'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link WorksheetGroup} result object.\n *\n * @remarks\n * Unknown members, prototypes, and class instances are admitted. Arrays are\n * refused. Optional members may be absent or read as `undefined`.\n *\n * @param value - The value to test\n * @returns True if every published worksheet-group member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isWorksheetGroup } from '@orkestrel/rater'\n *\n * isWorksheetGroup({ id: 'charges', applied: true, value: 100, factors: [] }) // true\n * ```\n */\nexport function isWorksheetGroup(value: unknown): value is WorksheetGroup {\n\treturn objectOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\tapplied: isBoolean,\n\t\t\tvalue: isNumber,\n\t\t\tfactors: arrayOf(isWorksheetFactor),\n\t\t},\n\t\t['name', 'description'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link Step} result object.\n *\n * @remarks\n * Unknown members, prototypes, and class instances are admitted. Arrays are\n * refused. Optional members may be absent or read as `undefined`.\n *\n * @param value - The value to test\n * @returns True if every published worksheet-step member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isStep } from '@orkestrel/rater'\n *\n * isStep({ stage: 'total', value: 100 }) // true\n * ```\n */\nexport function isStep(value: unknown): value is Step {\n\treturn objectOf(\n\t\t{\n\t\t\tstage: isStage,\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tvalue: isNumber,\n\t\t\texpression: isString,\n\t\t},\n\t\t['id', 'name', 'expression'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link Worksheet} result object.\n *\n * @remarks\n * Unknown members, prototypes, and class instances are admitted. Arrays are\n * refused. Optional members may be absent or read as `undefined`.\n *\n * @param value - The value to test\n * @returns True if every published worksheet member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isWorksheet } from '@orkestrel/rater'\n *\n * isWorksheet({ id: 'quote', name: 'Quote', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: true }) // true\n * ```\n */\nexport function isWorksheet(value: unknown): value is Worksheet {\n\treturn objectOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\taggregation: isAggregation,\n\t\t\tprecision: isNumber,\n\t\t\tvalue: isNumber,\n\t\t\tgroups: arrayOf(isWorksheetGroup),\n\t\t\tsteps: arrayOf(isStep),\n\t\t\ttrace: arrayOf(isString),\n\t\t\terrors: arrayOf(isString),\n\t\t\tsuccess: isBoolean,\n\t\t},\n\t\t['precision'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link LineResult} object.\n *\n * @remarks\n * Unknown members, prototypes, and class instances are admitted. Arrays are\n * refused. `amount` may be absent or read as `undefined`; when present it stays\n * a plain JavaScript `number` without a finite or range refinement. The line's\n * outcome lives on `worksheet`, so `worksheet.success` carries it.\n *\n * @param value - The value to test\n * @returns True if every published line-result member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isLineResult } from '@orkestrel/rater'\n *\n * isLineResult({ id: 'base', name: 'Base', worksheet: { id: 'base', name: 'Base', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: false } }) // true\n * ```\n */\nexport function isLineResult(value: unknown): value is LineResult {\n\treturn objectOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tamount: isNumber,\n\t\t\tworksheet: isWorksheet,\n\t\t},\n\t\t['amount'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link RatingResult} object.\n *\n * @remarks\n * Use this guard for a result returned by a borrowed `RaterInterface`. Unknown\n * members, prototypes, and class instances are admitted. Arrays are refused.\n * `total` may be absent or read as `undefined`; when present it stays a plain\n * JavaScript `number` without a finite or range refinement.\n *\n * @param value - The value to test\n * @returns True if every published rating-result member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isRatingResult } from '@orkestrel/rater'\n *\n * isRatingResult({ lines: [], success: true }) // true\n * ```\n */\nexport function isRatingResult(value: unknown): value is RatingResult {\n\treturn objectOf(\n\t\t{\n\t\t\tlines: arrayOf(isLineResult),\n\t\t\ttotal: isNumber,\n\t\t\tsuccess: isBoolean,\n\t\t},\n\t\t['total'],\n\t)(value)\n}\n","import type {\n\tCheck,\n\tCheckResult,\n\tFactor,\n\tFactorResult,\n\tGroupResult,\n\tQuantitativeDefinition,\n\tQuantitativeResult,\n} from '@orkestrel/reason'\nimport type {\n\tEvidence,\n\tLineDefinition,\n\tLineResult,\n\tRatingDefinition,\n\tStage,\n\tStep,\n\tWorksheet,\n\tWorksheetFactor,\n\tWorksheetGroup,\n} from './types.js'\nimport { formatField } from '@orkestrel/reason'\n\n/**\n * Builds a {@link LineDefinition}.\n *\n * @param id - The line id\n * @param name - The display name\n * @param rate - The line's quantitative rating definition\n * @param overrides - Optional {@link LineDefinition} fields merged over the defaults\n * @returns A fresh line definition\n *\n * @example\n * ```ts\n * import { createQuantitativeDefinition } from '@orkestrel/reason'\n * import { buildLineDefinition } from '@orkestrel/rater'\n *\n * buildLineDefinition('base', 'Base Amount', createQuantitativeDefinition('base', 'Base', []))\n * ```\n */\nexport function buildLineDefinition(\n\tid: string,\n\tname: string,\n\trate: QuantitativeDefinition,\n\toverrides?: Partial<Omit<LineDefinition, 'id' | 'name' | 'rate'>>,\n): LineDefinition {\n\treturn { id, name, rate, ...overrides }\n}\n\n/**\n * Builds a {@link RatingDefinition}.\n *\n * @param id - The rating id\n * @param name - The display name\n * @param lines - The rating's ordered lines\n * @param overrides - Optional {@link RatingDefinition} fields merged over the defaults\n * @returns A fresh rating definition\n *\n * @example\n * ```ts\n * import { buildLineDefinition, buildRatingDefinition } from '@orkestrel/rater'\n * import { createQuantitativeDefinition } from '@orkestrel/reason'\n *\n * buildRatingDefinition('r1', 'Rating', [\n * \tbuildLineDefinition('base', 'Base Amount', createQuantitativeDefinition('base', 'Base', [])),\n * ])\n * ```\n */\nexport function buildRatingDefinition(\n\tid: string,\n\tname: string,\n\tlines: readonly LineDefinition[],\n\toverrides?: Partial<Omit<RatingDefinition, 'id' | 'name' | 'lines'>>,\n): RatingDefinition {\n\treturn { id, name, lines, ...overrides }\n}\n\n/**\n * Builds an {@link Evidence} row from an evaluated {@link Check}.\n *\n * @param check - The authored check\n * @param actual - The resolved subject value\n * @param met - If `true`, the check was met; if `false`, it was not; absent when the check\n * was not yet evaluated\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh evidence row\n *\n * @example\n * ```ts\n * import { createCheck } from '@orkestrel/reason'\n * import { buildEvidence } from '@orkestrel/rater'\n *\n * const entry = createCheck('age', 'above', 18)\n * buildEvidence(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }\n * ```\n */\nexport function buildEvidence(\n\tcheck: Check,\n\tactual: unknown,\n\tmet: boolean | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): Evidence {\n\tconst field = formatField(check.field)\n\treturn {\n\t\tfield: check.field,\n\t\t...(labels?.[field] === undefined ? {} : { label: labels[field] }),\n\t\tcomparison: check.operator,\n\t\texpected: check.value,\n\t\tactual,\n\t\t...(met === undefined ? {} : { met }),\n\t}\n}\n\n/**\n * Builds one evidence row per authored check of a quantitative factor, joined\n * to that check's evaluated result.\n *\n * @param checks - The factor's authored checks\n * @param results - The corresponding {@link CheckResult}s, in the same order\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh list of evidence rows, one per authored check\n *\n * @example\n * ```ts\n * import { createCheck } from '@orkestrel/reason'\n * import { buildEvidenceRows } from '@orkestrel/rater'\n *\n * const checks = [createCheck('age', 'above', 18)]\n * buildEvidenceRows(checks, [{ field: 'age', met: true, actual: 25 }])\n * ```\n */\nexport function buildEvidenceRows(\n\tchecks: readonly Check[] | undefined,\n\tresults: readonly CheckResult[] | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): readonly Evidence[] {\n\treturn (checks ?? []).map((check, index) => {\n\t\tconst result = results?.[index]\n\t\treturn buildEvidence(check, result?.actual, result?.met, labels)\n\t})\n}\n\n/**\n * Joins one authored quantitative factor to its evaluated {@link FactorResult}.\n *\n * @param definition - The authored factor\n * @param results - The group's factor results\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet factor\n *\n * @example\n * ```ts\n * import { createFieldFactor } from '@orkestrel/reason'\n * import { buildWorksheetFactor } from '@orkestrel/rater'\n *\n * const factor = createFieldFactor('seats', 'seats')\n * buildWorksheetFactor(factor, [{ id: 'seats', applied: true, value: 10 }])\n * ```\n */\nexport function buildWorksheetFactor(\n\tdefinition: Factor,\n\tresults: readonly FactorResult[],\n\tlabels?: Readonly<Record<string, string>>,\n): WorksheetFactor {\n\tconst result = results.find((entry) => entry.id === definition.id)\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\t...(definition.description === undefined ? {} : { description: definition.description }),\n\t\tapplied: result?.applied ?? false,\n\t\t...(result?.value === undefined ? {} : { value: result.value }),\n\t\tevidence: buildEvidenceRows(definition.checks, result?.checks, labels),\n\t}\n}\n\n/**\n * Joins one authored quantitative group to its evaluated {@link GroupResult}.\n *\n * @param definition - The authored group\n * @param results - The definition's group results\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet group\n *\n * @example\n * ```ts\n * import { createFactorGroup, createStaticFactor } from '@orkestrel/reason'\n * import { buildWorksheetGroup } from '@orkestrel/rater'\n *\n * const group = createFactorGroup('charge', 'sum', [createStaticFactor('base', 100)])\n * buildWorksheetGroup(group, [{ id: 'charge', applied: true, value: 100, factors: [] }])\n * ```\n */\nexport function buildWorksheetGroup(\n\tdefinition: QuantitativeDefinition['groups'][number],\n\tresults: readonly GroupResult[],\n\tlabels?: Readonly<Record<string, string>>,\n): WorksheetGroup {\n\tconst result = results.find((entry) => entry.id === definition.id)\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\t...(definition.description === undefined ? {} : { description: definition.description }),\n\t\tapplied: result?.applied ?? false,\n\t\tvalue: result?.value ?? 0,\n\t\tfactors: definition.factors.map((factor) =>\n\t\t\tbuildWorksheetFactor(factor, result?.factors ?? [], labels),\n\t\t),\n\t}\n}\n\n/**\n * Builds one display-neutral {@link Step} row.\n *\n * @param stage - The step's stage\n * @param id - The stage's authored id, when it has one\n * @param name - The stage's authored name, when it has one\n * @param value - The stage's resolved value\n * @param expression - A display-neutral expression string\n * @returns A fresh step\n *\n * @example\n * ```ts\n * import { buildWorksheetStep } from '@orkestrel/rater'\n *\n * buildWorksheetStep('total', 'quote', 'Quote', 110, 'sum = 110')\n * // { stage: 'total', id: 'quote', name: 'Quote', value: 110, expression: 'sum = 110' }\n * ```\n */\nexport function buildWorksheetStep(\n\tstage: Stage,\n\tid: string | undefined,\n\tname: string | undefined,\n\tvalue: number,\n\texpression: string,\n): Step {\n\treturn {\n\t\tstage,\n\t\t...(id === undefined ? {} : { id }),\n\t\t...(name === undefined ? {} : { name }),\n\t\tvalue,\n\t\texpression,\n\t}\n}\n\n/**\n * Builds the ordered {@link Step} rows for a resolved {@link Worksheet}.\n *\n * @param definition - The authored quantitative definition\n * @param result - The evaluated quantitative result\n * @param groups - The definition's already-joined worksheet groups\n * @returns A fresh, ordered list of steps: applied factors, then groups, then the total\n *\n * @example\n * ```ts\n * import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'\n * import { buildWorksheetGroup, buildWorksheetSteps } from '@orkestrel/rater'\n *\n * const definition = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)\n * if (result.reasoning === 'quantitative') {\n * \tbuildWorksheetSteps(definition, result, definition.groups.map((group) => buildWorksheetGroup(group, result.groups)))\n * }\n * ```\n */\nexport function buildWorksheetSteps(\n\tdefinition: QuantitativeDefinition,\n\tresult: QuantitativeResult,\n\tgroups: readonly WorksheetGroup[],\n): readonly Step[] {\n\tconst output: Step[] = []\n\tfor (const group of groups) {\n\t\tfor (const factor of group.factors) {\n\t\t\tif (factor.applied && factor.value !== undefined) {\n\t\t\t\toutput.push(\n\t\t\t\t\tbuildWorksheetStep(\n\t\t\t\t\t\t'factor',\n\t\t\t\t\t\tfactor.id,\n\t\t\t\t\t\tfactor.name,\n\t\t\t\t\t\tfactor.value,\n\t\t\t\t\t\t`${factor.id} = ${factor.value}`,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\toutput.push(\n\t\t\tbuildWorksheetStep(\n\t\t\t\t'group',\n\t\t\t\tgroup.id,\n\t\t\t\tgroup.name,\n\t\t\t\tgroup.value,\n\t\t\t\t`${group.id} = ${group.value}`,\n\t\t\t),\n\t\t)\n\t}\n\toutput.push(\n\t\tbuildWorksheetStep(\n\t\t\t'total',\n\t\t\tdefinition.id,\n\t\t\tdefinition.name,\n\t\t\tresult.value,\n\t\t\t`${definition.aggregation} = ${result.value}`,\n\t\t),\n\t)\n\treturn output\n}\n\n/**\n * Joins a {@link QuantitativeDefinition} and its {@link QuantitativeResult} into\n * a {@link Worksheet} — the rating audit trail.\n *\n * @param definition - The authored quantitative definition\n * @param result - The evaluated quantitative result\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet\n *\n * @example\n * ```ts\n * import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'\n * import { buildWorksheet } from '@orkestrel/rater'\n *\n * const definition = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)\n * if (result.reasoning === 'quantitative') buildWorksheet(definition, result) // the full audit trail\n * ```\n */\nexport function buildWorksheet(\n\tdefinition: QuantitativeDefinition,\n\tresult: QuantitativeResult,\n\tlabels?: Readonly<Record<string, string>>,\n): Worksheet {\n\tconst groups = definition.groups.map((group) => buildWorksheetGroup(group, result.groups, labels))\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\taggregation: definition.aggregation,\n\t\t...(definition.precision === undefined ? {} : { precision: definition.precision }),\n\t\tvalue: result.value,\n\t\tgroups,\n\t\tsteps: buildWorksheetSteps(definition, result, groups),\n\t\ttrace: result.trace,\n\t\terrors: result.errors,\n\t\tsuccess: result.success,\n\t}\n}\n\n/**\n * Builds a rated {@link LineResult} from a line's evaluated\n * {@link QuantitativeResult}.\n *\n * @param definition - The authored line definition\n * @param result - The evaluated quantitative result\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh line result\n *\n * @example\n * ```ts\n * import { createQuantitativeDefinition, createQuantitativeReasoner, createReason } from '@orkestrel/reason'\n * import { buildLineDefinition, buildLineResult } from '@orkestrel/rater'\n *\n * const line = buildLineDefinition('base', 'Base', createQuantitativeDefinition('base', 'Base', []))\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, line.rate)\n * if (result.reasoning === 'quantitative') buildLineResult(line, result) // amount present only on a successful worksheet\n * ```\n */\nexport function buildLineResult(\n\tdefinition: LineDefinition,\n\tresult: QuantitativeResult,\n\tlabels?: Readonly<Record<string, string>>,\n): LineResult {\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\t...(result.success ? { amount: result.value } : {}),\n\t\tworksheet: buildWorksheet(definition.rate, result, labels),\n\t}\n}\n\n/**\n * Sums defined line amounts.\n *\n * @param lines - The rated lines\n * @returns The sum of every line's `amount`, or `undefined` when no line has one\n *\n * @example\n * ```ts\n * import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'\n * import { buildLineDefinition, buildLineResult, sumAmounts } from '@orkestrel/rater'\n *\n * const rate = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])\n * const line = buildLineDefinition('base', 'Base', rate)\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, rate)\n * if (result.reasoning === 'quantitative') sumAmounts([buildLineResult(line, result)]) // 100\n * ```\n */\nexport function sumAmounts(lines: readonly LineResult[]): number | undefined {\n\tlet total = 0\n\tlet count = 0\n\tfor (const rated of lines) {\n\t\tif (rated.amount !== undefined) {\n\t\t\ttotal += rated.amount\n\t\t\tcount += 1\n\t\t}\n\t}\n\treturn count === 0 ? undefined : total\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tQuantitativeDefinition,\n\tQuantitativeResult,\n\tReasonInterface,\n\tSubject,\n} from '@orkestrel/reason'\nimport type {\n\tLineDefinition,\n\tLineResult,\n\tRaterEventMap,\n\tRaterInterface,\n\tRaterOptions,\n\tRatingDefinition,\n\tRatingResult,\n\tTotalHandler,\n} from './types.js'\nimport { Emitter } from '@orkestrel/emitter'\nimport { arrayOf, isArray, isRecord } from '@orkestrel/contract'\nimport { buildErrorResult, createQuantitativeReasoner, createReason } from '@orkestrel/reason'\nimport { RaterError } from './errors.js'\nimport { buildLineResult, sumAmounts } from './helpers.js'\nimport { isLineDefinition, isRatingDefinition } from './validators.js'\n\n/**\n * Orchestrates rating — owns (or receives) the shared quantitative\n * reasoning engine and projects results into the rating domain vocabulary.\n *\n * @remarks\n * When no `engine` is injected, `Rater` builds and OWNS its own\n * quantitative-only engine (`bail: false`) — Rater performs NO evaluation\n * arithmetic of its own; it only orchestrates and projects. The\n * array-of-lines `rate` overload is declared FIRST so a plain line list\n * resolves to that form. `destroy()` destroys an OWNED engine, then the\n * emitter LAST; an INJECTED engine is never destroyed. Afterwards every\n * other method throws {@link RaterError} `'DESTROYED'`.\n *\n * @example\n * ```ts\n * import { Rater } from '@orkestrel/rater'\n *\n * const rater = new Rater()\n * rater.rate([], { id: 'subject-1' }) // { lines: [], success: true }\n * rater.destroy()\n * ```\n */\nexport class Rater implements RaterInterface {\n\treadonly #emitter: Emitter<RaterEventMap>\n\treadonly #engine: ReasonInterface\n\treadonly #owned: boolean\n\treadonly #total: TotalHandler | undefined\n\treadonly #labels: Readonly<Record<string, string>> | undefined\n\t#destroyed = false\n\n\tconstructor(options?: RaterOptions) {\n\t\tthis.#emitter = new Emitter<RaterEventMap>({\n\t\t\t...(options?.on === undefined ? {} : { on: options.on }),\n\t\t\t...(options?.error === undefined ? {} : { error: options.error }),\n\t\t})\n\t\tif (options?.engine === undefined) {\n\t\t\tthis.#engine = createReason({ reasoners: [createQuantitativeReasoner()], bail: false })\n\t\t\tthis.#owned = true\n\t\t} else {\n\t\t\tthis.#engine = options.engine\n\t\t\tthis.#owned = false\n\t\t}\n\t\tthis.#total = options?.total\n\t\tthis.#labels = options?.labels\n\t}\n\n\tget emitter(): EmitterInterface<RaterEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t// Array overload first so a plain line list resolves to that form.\n\trate(lines: readonly LineDefinition[], subject: Subject): RatingResult\n\trate(definition: RatingDefinition, subject: Subject): RatingResult\n\trate(input: readonly LineDefinition[] | RatingDefinition, subject: Subject): RatingResult {\n\t\tthis.#ensureAlive()\n\t\tconst lines = this.#normalize(input)\n\t\tif (!isRecord(subject)) throw new RaterError('MISMATCH', 'Subject must be a record')\n\t\tconst results = lines.map((line) => this.#rateLine(line, subject))\n\t\tconst total = (this.#total ?? sumAmounts)(results)\n\t\tconst success = results.every((entry) => entry.worksheet.success)\n\t\tconst result: RatingResult = {\n\t\t\tlines: results,\n\t\t\t...(total === undefined ? {} : { total }),\n\t\t\tsuccess,\n\t\t}\n\t\tthis.#emitter.emit('rate', subject, result)\n\t\treturn result\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\t\tif (this.#owned) this.#engine.destroy()\n\t\tthis.#emitter.destroy()\n\t}\n\n\t#normalize(input: readonly LineDefinition[] | RatingDefinition): readonly LineDefinition[] {\n\t\tif (arrayOf(isLineDefinition)(input)) return input\n\t\tif (isRatingDefinition(input)) return input.lines\n\t\tthrow new RaterError(\n\t\t\t'DEFINITION',\n\t\t\t'Rating input must be an array of line definitions or a rating definition',\n\t\t)\n\t}\n\n\t#rateLine(line: LineDefinition, subject: Subject): LineResult {\n\t\tconst result = this.#reasonQuantitative(subject, line.rate)\n\t\treturn buildLineResult(line, result, this.#labels)\n\t}\n\n\t// Narrows the engine's tagged ReasonResult union down to a QuantitativeResult\n\t// for a definition the engine is guaranteed (by dispatch-by-reasoning, given\n\t// only the quantitative reasoner is ever registered) to resolve as such — a\n\t// defensive, total fallback rather than an assertion. A nonconforming\n\t// injected engine result missing (or with a non-array) `errors` contributes\n\t// no errors from that side rather than throwing.\n\t#reasonQuantitative(subject: Subject, definition: QuantitativeDefinition): QuantitativeResult {\n\t\tconst result = this.#engine.reason(subject, definition)\n\t\tif (result.reasoning === 'quantitative') return result\n\t\tconst failure = buildErrorResult(\n\t\t\tdefinition,\n\t\t\t`Expected quantitative result, got ${result.reasoning}`,\n\t\t)\n\t\tconst errors = [...(isArray<string>(result.errors) ? result.errors : []), ...failure.errors]\n\t\tif (failure.reasoning === 'quantitative') return { ...failure, errors }\n\t\treturn {\n\t\t\treasoning: 'quantitative',\n\t\t\tvalue: 0,\n\t\t\tgroups: [],\n\t\t\tcount: 0,\n\t\t\tsuccess: false,\n\t\t\ttrace: [],\n\t\t\terrors,\n\t\t}\n\t}\n\n\t#ensureAlive(): void {\n\t\tif (this.#destroyed) throw new RaterError('DESTROYED', 'Rater has been destroyed')\n\t}\n}\n","import type { RaterInterface, RaterOptions } from './types.js'\nimport { Rater } from './Rater.js'\n\n/**\n * Creates a rating orchestrator.\n *\n * @param options - Optional total handler, labels, injected engine, and emitter hooks\n * @returns A {@link RaterInterface}\n *\n * @example\n * ```ts\n * import { createRater } from '@orkestrel/rater'\n *\n * const rater = createRater()\n * rater.destroy()\n * ```\n */\nexport function createRater(options?: RaterOptions): RaterInterface {\n\treturn new Rater(options)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAiBA,IAAa,aAAb,cAAgC,MAAM;CACrC;CACA;CAEA,YAAY,MAAsB,SAAiB,SAA6C;EAC/F,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,IAAI,YAAY,KAAA,GAAW,KAAK,UAAU;CAC3C;AACD;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,aAAa,OAAqC;CACjE,OAAO,iBAAiB;AACzB;;;;;;;;;;;;;;;;;ACJA,IAAa,WAAA,GAAwB,oBAAA,UAAA,CAAU,UAAU,SAAS,OAAO;;;;;;;;;;;;;;;;;;;AAoBzE,SAAgB,iBAAiB,OAAyC;CACzE,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,aAAa,oBAAA;EACb,MAAM,kBAAA;EACN,UAAU,oBAAA;CACX,GACA,CAAC,eAAe,UAAU,CAC3B,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,mBAAmB,OAA2C;CAC7E,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,aAAa,oBAAA;EACb,QAAA,GAAO,oBAAA,QAAA,CAAQ,gBAAgB;EAC/B,UAAU,oBAAA;CACX,GACA,CAAC,eAAe,UAAU,CAC3B,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,WAAW,OAAmC;CAC7D,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,OAAO,kBAAA;EACP,OAAO,oBAAA;EACP,YAAY,kBAAA;EACZ,KAAK,oBAAA;CACN,GACA;EAAC;EAAS;EAAS;EAAc;CAAK,CACvC,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,kBAAkB,OAA0C;CAC3E,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,aAAa,oBAAA;EACb,SAAS,oBAAA;EACT,OAAO,oBAAA;EACP,WAAA,GAAU,oBAAA,QAAA,CAAQ,UAAU;CAC7B,GACA;EAAC;EAAQ;EAAe;CAAO,CAChC,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,iBAAiB,OAAyC;CACzE,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,aAAa,oBAAA;EACb,SAAS,oBAAA;EACT,OAAO,oBAAA;EACP,UAAA,GAAS,oBAAA,QAAA,CAAQ,iBAAiB;CACnC,GACA,CAAC,QAAQ,aAAa,CACvB,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,OAAO,OAA+B;CACrD,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,OAAO;EACP,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,OAAO,oBAAA;EACP,YAAY,oBAAA;CACb,GACA;EAAC;EAAM;EAAQ;CAAY,CAC5B,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,YAAY,OAAoC;CAC/D,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,aAAa,kBAAA;EACb,WAAW,oBAAA;EACX,OAAO,oBAAA;EACP,SAAA,GAAQ,oBAAA,QAAA,CAAQ,gBAAgB;EAChC,QAAA,GAAO,oBAAA,QAAA,CAAQ,MAAM;EACrB,QAAA,GAAO,oBAAA,QAAA,CAAQ,oBAAA,QAAQ;EACvB,SAAA,GAAQ,oBAAA,QAAA,CAAQ,oBAAA,QAAQ;EACxB,SAAS,oBAAA;CACV,GACA,CAAC,WAAW,CACb,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,aAAa,OAAqC;CACjE,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,QAAQ,oBAAA;EACR,WAAW;CACZ,GACA,CAAC,QAAQ,CACV,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,eAAe,OAAuC;CACrE,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,QAAA,GAAO,oBAAA,QAAA,CAAQ,YAAY;EAC3B,OAAO,oBAAA;EACP,SAAS,oBAAA;CACV,GACA,CAAC,OAAO,CACT,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;AC7RA,SAAgB,oBACf,IACA,MACA,MACA,WACiB;CACjB,OAAO;EAAE;EAAI;EAAM;EAAM,GAAG;CAAU;AACvC;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,sBACf,IACA,MACA,OACA,WACmB;CACnB,OAAO;EAAE;EAAI;EAAM;EAAO,GAAG;CAAU;AACxC;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,cACf,OACA,QACA,KACA,QACW;CACX,MAAM,SAAA,GAAQ,kBAAA,YAAA,CAAY,MAAM,KAAK;CACrC,OAAO;EACN,OAAO,MAAM;EACb,GAAI,SAAS,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,OAAO,OAAO;EAChE,YAAY,MAAM;EAClB,UAAU,MAAM;EAChB;EACA,GAAI,QAAQ,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI;CACpC;AACD;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,kBACf,QACA,SACA,QACsB;CACtB,QAAQ,UAAU,CAAC,EAAA,CAAG,KAAK,OAAO,UAAU;EAC3C,MAAM,SAAS,UAAU;EACzB,OAAO,cAAc,OAAO,QAAQ,QAAQ,QAAQ,KAAK,MAAM;CAChE,CAAC;AACF;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,qBACf,YACA,SACA,QACkB;CAClB,MAAM,SAAS,QAAQ,MAAM,UAAU,MAAM,OAAO,WAAW,EAAE;CACjE,OAAO;EACN,IAAI,WAAW;EACf,MAAM,WAAW;EACjB,GAAI,WAAW,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,WAAW,YAAY;EACtF,SAAS,QAAQ,WAAW;EAC5B,GAAI,QAAQ,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,OAAO,MAAM;EAC7D,UAAU,kBAAkB,WAAW,QAAQ,QAAQ,QAAQ,MAAM;CACtE;AACD;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,oBACf,YACA,SACA,QACiB;CACjB,MAAM,SAAS,QAAQ,MAAM,UAAU,MAAM,OAAO,WAAW,EAAE;CACjE,OAAO;EACN,IAAI,WAAW;EACf,MAAM,WAAW;EACjB,GAAI,WAAW,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,WAAW,YAAY;EACtF,SAAS,QAAQ,WAAW;EAC5B,OAAO,QAAQ,SAAS;EACxB,SAAS,WAAW,QAAQ,KAAK,WAChC,qBAAqB,QAAQ,QAAQ,WAAW,CAAC,GAAG,MAAM,CAC3D;CACD;AACD;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,mBACf,OACA,IACA,MACA,OACA,YACO;CACP,OAAO;EACN;EACA,GAAI,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,GAAG;EACjC,GAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK;EACrC;EACA;CACD;AACD;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,oBACf,YACA,QACA,QACkB;CAClB,MAAM,SAAiB,CAAC;CACxB,KAAK,MAAM,SAAS,QAAQ;EAC3B,KAAK,MAAM,UAAU,MAAM,SAC1B,IAAI,OAAO,WAAW,OAAO,UAAU,KAAA,GACtC,OAAO,KACN,mBACC,UACA,OAAO,IACP,OAAO,MACP,OAAO,OACP,GAAG,OAAO,GAAG,KAAK,OAAO,OAC1B,CACD;EAGF,OAAO,KACN,mBACC,SACA,MAAM,IACN,MAAM,MACN,MAAM,OACN,GAAG,MAAM,GAAG,KAAK,MAAM,OACxB,CACD;CACD;CACA,OAAO,KACN,mBACC,SACA,WAAW,IACX,WAAW,MACX,OAAO,OACP,GAAG,WAAW,YAAY,KAAK,OAAO,OACvC,CACD;CACA,OAAO;AACR;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,eACf,YACA,QACA,QACY;CACZ,MAAM,SAAS,WAAW,OAAO,KAAK,UAAU,oBAAoB,OAAO,OAAO,QAAQ,MAAM,CAAC;CACjG,OAAO;EACN,IAAI,WAAW;EACf,MAAM,WAAW;EACjB,aAAa,WAAW;EACxB,GAAI,WAAW,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW,WAAW,UAAU;EAChF,OAAO,OAAO;EACd;EACA,OAAO,oBAAoB,YAAY,QAAQ,MAAM;EACrD,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,SAAS,OAAO;CACjB;AACD;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,gBACf,YACA,QACA,QACa;CACb,OAAO;EACN,IAAI,WAAW;EACf,MAAM,WAAW;EACjB,GAAI,OAAO,UAAU,EAAE,QAAQ,OAAO,MAAM,IAAI,CAAC;EACjD,WAAW,eAAe,WAAW,MAAM,QAAQ,MAAM;CAC1D;AACD;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,WAAW,OAAkD;CAC5E,IAAI,QAAQ;CACZ,IAAI,QAAQ;CACZ,KAAK,MAAM,SAAS,OACnB,IAAI,MAAM,WAAW,KAAA,GAAW;EAC/B,SAAS,MAAM;EACf,SAAS;CACV;CAED,OAAO,UAAU,IAAI,KAAA,IAAY;AAClC;;;;;;;;;;;;;;;;;;;;;;;;;ACrWA,IAAa,QAAb,MAA6C;CAC5C;CACA;CACA;CACA;CACA;CACA,aAAa;CAEb,YAAY,SAAwB;EACnC,KAAKA,WAAW,IAAI,mBAAA,QAAuB;GAC1C,GAAI,SAAS,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI,QAAQ,GAAG;GACtD,GAAI,SAAS,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,QAAQ,MAAM;EAChE,CAAC;EACD,IAAI,SAAS,WAAW,KAAA,GAAW;GAClC,KAAKC,WAAAA,GAAU,kBAAA,aAAA,CAAa;IAAE,WAAW,EAAA,GAAC,kBAAA,2BAAA,CAA2B,CAAC;IAAG,MAAM;GAAM,CAAC;GACtF,KAAKC,SAAS;EACf,OAAO;GACN,KAAKD,UAAU,QAAQ;GACvB,KAAKC,SAAS;EACf;EACA,KAAKC,SAAS,SAAS;EACvB,KAAKC,UAAU,SAAS;CACzB;CAEA,IAAI,UAA2C;EAC9C,OAAO,KAAKJ;CACb;CAKA,KAAK,OAAqD,SAAgC;EACzF,KAAKK,aAAa;EAClB,MAAM,QAAQ,KAAKC,WAAW,KAAK;EACnC,IAAI,EAAA,GAAC,oBAAA,SAAA,CAAS,OAAO,GAAG,MAAM,IAAI,WAAW,YAAY,0BAA0B;EACnF,MAAM,UAAU,MAAM,KAAK,SAAS,KAAKC,UAAU,MAAM,OAAO,CAAC;EACjE,MAAM,SAAS,KAAKJ,UAAU,WAAA,CAAY,OAAO;EACjD,MAAM,UAAU,QAAQ,OAAO,UAAU,MAAM,UAAU,OAAO;EAChE,MAAM,SAAuB;GAC5B,OAAO;GACP,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;GACvC;EACD;EACA,KAAKH,SAAS,KAAK,QAAQ,SAAS,MAAM;EAC1C,OAAO;CACR;CAEA,UAAgB;EACf,IAAI,KAAKQ,YAAY;EACrB,KAAKA,aAAa;EAClB,IAAI,KAAKN,QAAQ,KAAKD,QAAQ,QAAQ;EACtC,KAAKD,SAAS,QAAQ;CACvB;CAEA,WAAW,OAAgF;EAC1F,KAAA,GAAI,oBAAA,QAAA,CAAQ,gBAAgB,CAAC,CAAC,KAAK,GAAG,OAAO;EAC7C,IAAI,mBAAmB,KAAK,GAAG,OAAO,MAAM;EAC5C,MAAM,IAAI,WACT,cACA,0EACD;CACD;CAEA,UAAU,MAAsB,SAA8B;EAE7D,OAAO,gBAAgB,MADR,KAAKS,oBAAoB,SAAS,KAAK,IACzB,GAAQ,KAAKL,OAAO;CAClD;CAQA,oBAAoB,SAAkB,YAAwD;EAC7F,MAAM,SAAS,KAAKH,QAAQ,OAAO,SAAS,UAAU;EACtD,IAAI,OAAO,cAAc,gBAAgB,OAAO;EAChD,MAAM,WAAA,GAAU,kBAAA,iBAAA,CACf,YACA,qCAAqC,OAAO,WAC7C;EACA,MAAM,SAAS,CAAC,IAAA,GAAI,oBAAA,QAAA,CAAgB,OAAO,MAAM,IAAI,OAAO,SAAS,CAAC,GAAI,GAAG,QAAQ,MAAM;EAC3F,IAAI,QAAQ,cAAc,gBAAgB,OAAO;GAAE,GAAG;GAAS;EAAO;EACtE,OAAO;GACN,WAAW;GACX,OAAO;GACP,QAAQ,CAAC;GACT,OAAO;GACP,SAAS;GACT,OAAO,CAAC;GACR;EACD;CACD;CAEA,eAAqB;EACpB,IAAI,KAAKO,YAAY,MAAM,IAAI,WAAW,aAAa,0BAA0B;CAClF;AACD;;;;;;;;;;;;;;;;;AC9HA,SAAgB,YAAY,SAAwC;CACnE,OAAO,IAAI,MAAM,OAAO;AACzB"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/core/errors.ts","../../../src/core/validators.ts","../../../src/core/helpers.ts","../../../src/core/Rater.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { RaterErrorCode } from './types.js'\nimport { isInstance } from '@orkestrel/contract'\n\n/**\n * Represents a coded programmer error thrown by the rating layer.\n *\n * @remarks\n * `DEFINITION` — the `rate` input failed both the array-of-lines and rating\n * definition validation. `MISMATCH` — a rated subject is not a record.\n * `DESTROYED` — use of a destroyed entity. `context` carries optional\n * structured detail beside the message.\n *\n * @example\n * ```ts\n * import { RaterError } from '@orkestrel/rater'\n *\n * throw new RaterError('MISMATCH', 'Subject must be a record')\n * ```\n */\nexport class RaterError extends Error {\n\treadonly code: RaterErrorCode\n\treadonly context?: Readonly<Record<string, unknown>>\n\n\tconstructor(code: RaterErrorCode, message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message)\n\t\tthis.name = 'RaterError'\n\t\tthis.code = code\n\t\tif (context !== undefined) this.context = context\n\t}\n}\n\n/**\n * Narrows a caught value to a {@link RaterError}.\n *\n * @param value - The caught value to test\n * @returns True if `value` is a `RaterError`; false otherwise\n *\n * @example\n * ```ts\n * import { isRaterError, RaterError } from '@orkestrel/rater'\n *\n * try {\n * \tthrow new RaterError('DESTROYED', 'Rater has been destroyed')\n * } catch (error) {\n * \tif (isRaterError(error)) error.code // 'DESTROYED'\n * }\n * ```\n */\nexport function isRaterError(value: unknown): value is RaterError {\n\treturn isInstance(value, RaterError)\n}\n","import type { Guard } from '@orkestrel/contract'\nimport type {\n\tEvidence,\n\tLineDefinition,\n\tLineResult,\n\tRatingDefinition,\n\tRatingResult,\n\tStage,\n\tStep,\n\tWorksheet,\n\tWorksheetFactor,\n\tWorksheetGroup,\n} from './types.js'\nimport {\n\tarrayOf,\n\tisBoolean,\n\tisJSONValue,\n\tisNumber,\n\tisString,\n\tliteralOf,\n\tobjectOf,\n\trecordOf,\n} from '@orkestrel/contract'\nimport {\n\tisAggregation,\n\tisComparison,\n\tisFieldPath,\n\tisQuantitativeDefinition,\n} from '@orkestrel/reason'\n\n/**\n * Determines whether a value is a {@link Stage} literal.\n *\n * @remarks\n * A scalar union has no open or exact axis, so this guard accepts one of the\n * `Stage` literals and leaves nothing unchecked.\n *\n * @param value - The value to test\n * @returns True if `value` is `'factor'`, `'group'`, or `'total'`; false otherwise\n *\n * @example\n * ```ts\n * import { isStage } from '@orkestrel/rater'\n *\n * isStage('group') // true\n * isStage('step') // false\n * ```\n */\nexport const isStage: Guard<Stage> = literalOf('factor', 'group', 'total')\n\n/**\n * Determines whether a value is an exact {@link LineDefinition} record.\n *\n * @remarks\n * Every `LineDefinition` member is checked, including its quantitative\n * definition. Total guard: adversarial input (cycles, hostile prototypes)\n * returns `false`, never throws. The record shape is exact — this package owns\n * the authored input record, so an extra key fails and nothing is left\n * unchecked.\n *\n * @param value - The value to test\n * @returns True if `value` is a `LineDefinition`; false otherwise\n *\n * @example\n * ```ts\n * import { createQuantitativeDefinition } from '@orkestrel/reason'\n * import { isLineDefinition } from '@orkestrel/rater'\n *\n * isLineDefinition({ id: 'base', name: 'Base', rate: createQuantitativeDefinition('base', 'Base', []) }) // true\n * ```\n */\nexport function isLineDefinition(value: unknown): value is LineDefinition {\n\treturn recordOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\trate: isQuantitativeDefinition,\n\t\t\tmetadata: isJSONValue,\n\t\t},\n\t\t['description', 'metadata'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an exact {@link RatingDefinition} record.\n *\n * @remarks\n * Every `RatingDefinition` member is checked, including each nested line\n * definition. Total guard: adversarial input (cycles, hostile prototypes)\n * returns `false`, never throws. The record shape is exact — this package owns\n * the authored input record, so an extra key fails and nothing is left\n * unchecked.\n *\n * @param value - The value to test\n * @returns True if `value` is a `RatingDefinition`; false otherwise\n *\n * @example\n * ```ts\n * import { isRatingDefinition } from '@orkestrel/rater'\n *\n * isRatingDefinition({ id: 'r1', name: 'Rating', lines: [] }) // true\n * ```\n */\nexport function isRatingDefinition(value: unknown): value is RatingDefinition {\n\treturn recordOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\tlines: arrayOf(isLineDefinition),\n\t\t\tmetadata: isJSONValue,\n\t\t},\n\t\t['description', 'metadata'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open result-side {@link Evidence} object.\n *\n * @remarks\n * Optional `field` is checked as a `FieldPath`, `label` as a string,\n * `comparison` as a `Comparison`, and `met` as a boolean, each when defined.\n * Unknown members, prototypes, and class instances are admitted. Arrays are\n * refused. `expected` and `actual` are `unknown`, so this guard does not read or\n * check them and a borrowed result is never narrowed on them; either member may\n * also be absent because `objectOf` reads declared members rather than\n * requiring or enumerating own keys.\n *\n * @param value - The value to test\n * @returns True if every checked `Evidence` member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isEvidence } from '@orkestrel/rater'\n *\n * isEvidence({ field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }) // true\n * ```\n */\nexport function isEvidence(value: unknown): value is Evidence {\n\treturn objectOf(\n\t\t{\n\t\t\tfield: isFieldPath,\n\t\t\tlabel: isString,\n\t\t\tcomparison: isComparison,\n\t\t\tmet: isBoolean,\n\t\t},\n\t\t['field', 'label', 'comparison', 'met'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link WorksheetFactor} result object.\n *\n * @remarks\n * `id`, the optional authored text, `applied`, an optional plain-number\n * `value`, and each `Evidence` entry are checked. Unknown members, prototypes,\n * and class instances are admitted and left unchecked, because a borrowed\n * result implementation may add them. Arrays are refused. Optional members may\n * be absent or read as `undefined`.\n *\n * @param value - The value to test\n * @returns True if every published worksheet-factor member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isWorksheetFactor } from '@orkestrel/rater'\n *\n * isWorksheetFactor({ id: 'base', applied: true, value: 100, evidence: [] }) // true\n * ```\n */\nexport function isWorksheetFactor(value: unknown): value is WorksheetFactor {\n\treturn objectOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\tapplied: isBoolean,\n\t\t\tvalue: isNumber,\n\t\t\tevidence: arrayOf(isEvidence),\n\t\t},\n\t\t['name', 'description', 'value'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link WorksheetGroup} result object.\n *\n * @remarks\n * `id`, the optional authored text, `applied`, a plain-number `value`, and each\n * `WorksheetFactor` entry are checked. Unknown members, prototypes, and class\n * instances are admitted and left unchecked, because a borrowed result\n * implementation may add them. Arrays are refused. Optional members may be\n * absent or read as `undefined`.\n *\n * @param value - The value to test\n * @returns True if every published worksheet-group member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isWorksheetGroup } from '@orkestrel/rater'\n *\n * isWorksheetGroup({ id: 'charges', applied: true, value: 100, factors: [] }) // true\n * ```\n */\nexport function isWorksheetGroup(value: unknown): value is WorksheetGroup {\n\treturn objectOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tdescription: isString,\n\t\t\tapplied: isBoolean,\n\t\t\tvalue: isNumber,\n\t\t\tfactors: arrayOf(isWorksheetFactor),\n\t\t},\n\t\t['name', 'description'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link Step} result object.\n *\n * @remarks\n * `stage` is checked through `isStage`, `id`, `name`, and `expression` when\n * defined, and `value` as a plain number. Unknown members, prototypes, and\n * class instances are admitted and left unchecked, because a borrowed result\n * implementation may add them. Arrays are refused. Optional members may be\n * absent or read as `undefined`.\n *\n * @param value - The value to test\n * @returns True if every published worksheet-step member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isStep } from '@orkestrel/rater'\n *\n * isStep({ stage: 'total', value: 100 }) // true\n * ```\n */\nexport function isStep(value: unknown): value is Step {\n\treturn objectOf(\n\t\t{\n\t\t\tstage: isStage,\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tvalue: isNumber,\n\t\t\texpression: isString,\n\t\t},\n\t\t['id', 'name', 'expression'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link Worksheet} result object.\n *\n * @remarks\n * Identity, `aggregation` through reason's `isAggregation`, an optional\n * plain-number `precision`, a plain-number `value`, the nested groups and steps,\n * `trace`, `errors`, and `success` are checked. Unknown members, prototypes, and\n * class instances are admitted and left unchecked, because a borrowed result\n * implementation may add them. Arrays are refused. Optional members may be\n * absent or read as `undefined`.\n *\n * @param value - The value to test\n * @returns True if every published worksheet member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isWorksheet } from '@orkestrel/rater'\n *\n * isWorksheet({ id: 'quote', name: 'Quote', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: true }) // true\n * ```\n */\nexport function isWorksheet(value: unknown): value is Worksheet {\n\treturn objectOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\taggregation: isAggregation,\n\t\t\tprecision: isNumber,\n\t\t\tvalue: isNumber,\n\t\t\tgroups: arrayOf(isWorksheetGroup),\n\t\t\tsteps: arrayOf(isStep),\n\t\t\ttrace: arrayOf(isString),\n\t\t\terrors: arrayOf(isString),\n\t\t\tsuccess: isBoolean,\n\t\t},\n\t\t['precision'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link LineResult} object.\n *\n * @remarks\n * Identity, an optional plain-number `amount`, and the nested `Worksheet` that\n * carries the line's outcome are checked. Unknown members, prototypes, and\n * class instances are admitted and left unchecked, because a borrowed result\n * implementation may add them, and so is the relationship between `amount` and\n * `worksheet.success`, which the published interface types independently.\n * Arrays are refused. `amount` may be absent or read as `undefined`; when\n * present it stays a plain JavaScript `number` without a finite or range\n * refinement.\n *\n * @param value - The value to test\n * @returns True if every published line-result member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isLineResult } from '@orkestrel/rater'\n *\n * isLineResult({ id: 'base', name: 'Base', worksheet: { id: 'base', name: 'Base', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: false } }) // true\n * ```\n */\nexport function isLineResult(value: unknown): value is LineResult {\n\treturn objectOf(\n\t\t{\n\t\t\tid: isString,\n\t\t\tname: isString,\n\t\t\tamount: isNumber,\n\t\t\tworksheet: isWorksheet,\n\t\t},\n\t\t['amount'],\n\t)(value)\n}\n\n/**\n * Determines whether a value is an open {@link RatingResult} object.\n *\n * @remarks\n * Use this guard for a result returned by a borrowed `RaterInterface`. Every\n * nested `LineResult`, an optional plain-number `total`, and a boolean `success`\n * are checked. Unknown members, prototypes, and class instances are admitted and\n * left unchecked, and so are the relationships among totals, lines, and success,\n * because the borrowed interface publishes only their member types. Arrays are\n * refused. `total` may be absent or read as `undefined`; when present it stays a\n * plain JavaScript `number` without a finite or range refinement.\n *\n * @param value - The value to test\n * @returns True if every published rating-result member conforms; false otherwise\n *\n * @example\n * ```ts\n * import { isRatingResult } from '@orkestrel/rater'\n *\n * isRatingResult({ lines: [], success: true }) // true\n * ```\n */\nexport function isRatingResult(value: unknown): value is RatingResult {\n\treturn objectOf(\n\t\t{\n\t\t\tlines: arrayOf(isLineResult),\n\t\t\ttotal: isNumber,\n\t\t\tsuccess: isBoolean,\n\t\t},\n\t\t['total'],\n\t)(value)\n}\n","import type {\n\tCheck,\n\tCheckResult,\n\tFactor,\n\tFactorResult,\n\tGroupResult,\n\tQuantitativeDefinition,\n\tQuantitativeResult,\n} from '@orkestrel/reason'\nimport type {\n\tEvidence,\n\tLineDefinition,\n\tLineResult,\n\tRatingDefinition,\n\tStage,\n\tStep,\n\tWorksheet,\n\tWorksheetFactor,\n\tWorksheetGroup,\n} from './types.js'\nimport { formatField } from '@orkestrel/reason'\n\n/**\n * Builds a fresh {@link LineDefinition} from a line id, a display name, and the\n * line's quantitative rating definition, with `overrides` merged over those\n * defaults.\n *\n * @param id - The line id\n * @param name - The display name\n * @param rate - The line's quantitative rating definition\n * @param overrides - Optional {@link LineDefinition} fields merged over the defaults\n * @returns A fresh line definition\n *\n * @example\n * ```ts\n * import { createQuantitativeDefinition } from '@orkestrel/reason'\n * import { buildLineDefinition } from '@orkestrel/rater'\n *\n * buildLineDefinition('base', 'Base Amount', createQuantitativeDefinition('base', 'Base', []))\n * ```\n */\nexport function buildLineDefinition(\n\tid: string,\n\tname: string,\n\trate: QuantitativeDefinition,\n\toverrides?: Partial<Omit<LineDefinition, 'id' | 'name' | 'rate'>>,\n): LineDefinition {\n\treturn { id, name, rate, ...overrides }\n}\n\n/**\n * Builds a fresh {@link RatingDefinition} from a rating id, a display name, and\n * the rating's ordered lines, with `overrides` merged over those defaults.\n *\n * @param id - The rating id\n * @param name - The display name\n * @param lines - The rating's ordered lines\n * @param overrides - Optional {@link RatingDefinition} fields merged over the defaults\n * @returns A fresh rating definition\n *\n * @example\n * ```ts\n * import { buildLineDefinition, buildRatingDefinition } from '@orkestrel/rater'\n * import { createQuantitativeDefinition } from '@orkestrel/reason'\n *\n * buildRatingDefinition('r1', 'Rating', [\n * \tbuildLineDefinition('base', 'Base Amount', createQuantitativeDefinition('base', 'Base', [])),\n * ])\n * ```\n */\nexport function buildRatingDefinition(\n\tid: string,\n\tname: string,\n\tlines: readonly LineDefinition[],\n\toverrides?: Partial<Omit<RatingDefinition, 'id' | 'name' | 'lines'>>,\n): RatingDefinition {\n\treturn { id, name, lines, ...overrides }\n}\n\n/**\n * Builds an {@link Evidence} row from an evaluated {@link Check}.\n *\n * @param check - The authored check\n * @param actual - The resolved subject value\n * @param met - If `true`, the check was met; if `false`, it was not; absent when the check\n * was not yet evaluated\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh evidence row\n *\n * @example\n * ```ts\n * import { createCheck } from '@orkestrel/reason'\n * import { buildEvidence } from '@orkestrel/rater'\n *\n * const entry = createCheck('age', 'above', 18)\n * buildEvidence(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }\n * ```\n */\nexport function buildEvidence(\n\tcheck: Check,\n\tactual: unknown,\n\tmet: boolean | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): Evidence {\n\tconst field = formatField(check.field)\n\treturn {\n\t\tfield: check.field,\n\t\t...(labels?.[field] === undefined ? {} : { label: labels[field] }),\n\t\tcomparison: check.operator,\n\t\texpected: check.value,\n\t\tactual,\n\t\t...(met === undefined ? {} : { met }),\n\t}\n}\n\n/**\n * Builds one evidence row per authored check of a quantitative factor, joined\n * to that check's evaluated result.\n *\n * @param checks - The factor's authored checks\n * @param results - The corresponding {@link CheckResult}s, in the same order\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh list of evidence rows, one per authored check\n *\n * @example\n * ```ts\n * import { createCheck } from '@orkestrel/reason'\n * import { buildEvidenceRows } from '@orkestrel/rater'\n *\n * const checks = [createCheck('age', 'above', 18)]\n * buildEvidenceRows(checks, [{ field: 'age', met: true, actual: 25 }])\n * ```\n */\nexport function buildEvidenceRows(\n\tchecks: readonly Check[] | undefined,\n\tresults: readonly CheckResult[] | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): readonly Evidence[] {\n\treturn (checks ?? []).map((check, index) => {\n\t\tconst result = results?.[index]\n\t\treturn buildEvidence(check, result?.actual, result?.met, labels)\n\t})\n}\n\n/**\n * Joins one authored quantitative factor to its evaluated {@link FactorResult}.\n *\n * @param definition - The authored factor\n * @param results - The group's factor results\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet factor\n *\n * @example\n * ```ts\n * import { createFieldFactor } from '@orkestrel/reason'\n * import { buildWorksheetFactor } from '@orkestrel/rater'\n *\n * const factor = createFieldFactor('seats', 'seats')\n * buildWorksheetFactor(factor, [{ id: 'seats', applied: true, value: 10 }])\n * ```\n */\nexport function buildWorksheetFactor(\n\tdefinition: Factor,\n\tresults: readonly FactorResult[],\n\tlabels?: Readonly<Record<string, string>>,\n): WorksheetFactor {\n\tconst result = results.find((entry) => entry.id === definition.id)\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\t...(definition.description === undefined ? {} : { description: definition.description }),\n\t\tapplied: result?.applied ?? false,\n\t\t...(result?.value === undefined ? {} : { value: result.value }),\n\t\tevidence: buildEvidenceRows(definition.checks, result?.checks, labels),\n\t}\n}\n\n/**\n * Joins one authored quantitative group to its evaluated {@link GroupResult}.\n *\n * @param definition - The authored group\n * @param results - The definition's group results\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet group\n *\n * @example\n * ```ts\n * import { createFactorGroup, createStaticFactor } from '@orkestrel/reason'\n * import { buildWorksheetGroup } from '@orkestrel/rater'\n *\n * const group = createFactorGroup('charge', 'sum', [createStaticFactor('base', 100)])\n * buildWorksheetGroup(group, [{ id: 'charge', applied: true, value: 100, factors: [] }])\n * ```\n */\nexport function buildWorksheetGroup(\n\tdefinition: QuantitativeDefinition['groups'][number],\n\tresults: readonly GroupResult[],\n\tlabels?: Readonly<Record<string, string>>,\n): WorksheetGroup {\n\tconst result = results.find((entry) => entry.id === definition.id)\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\t...(definition.description === undefined ? {} : { description: definition.description }),\n\t\tapplied: result?.applied ?? false,\n\t\tvalue: result?.value ?? 0,\n\t\tfactors: definition.factors.map((factor) =>\n\t\t\tbuildWorksheetFactor(factor, result?.factors ?? [], labels),\n\t\t),\n\t}\n}\n\n/**\n * Builds one display-neutral {@link Step} row.\n *\n * @param stage - The step's stage\n * @param id - The stage's authored id, when it has one\n * @param name - The stage's authored name, when it has one\n * @param value - The stage's resolved value\n * @param expression - A display-neutral expression string\n * @returns A fresh step\n *\n * @example\n * ```ts\n * import { buildWorksheetStep } from '@orkestrel/rater'\n *\n * buildWorksheetStep('total', 'quote', 'Quote', 110, 'sum = 110')\n * // { stage: 'total', id: 'quote', name: 'Quote', value: 110, expression: 'sum = 110' }\n * ```\n */\nexport function buildWorksheetStep(\n\tstage: Stage,\n\tid: string | undefined,\n\tname: string | undefined,\n\tvalue: number,\n\texpression: string,\n): Step {\n\treturn {\n\t\tstage,\n\t\t...(id === undefined ? {} : { id }),\n\t\t...(name === undefined ? {} : { name }),\n\t\tvalue,\n\t\texpression,\n\t}\n}\n\n/**\n * Builds the ordered {@link Step} rows for a resolved {@link Worksheet}.\n *\n * @param definition - The authored quantitative definition\n * @param result - The evaluated quantitative result\n * @param groups - The definition's already-joined worksheet groups\n * @returns A fresh, ordered list of steps: applied factors, then groups, then the total\n *\n * @example\n * ```ts\n * import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'\n * import { buildWorksheetGroup, buildWorksheetSteps } from '@orkestrel/rater'\n *\n * const definition = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)\n * if (result.reasoning === 'quantitative') {\n * \tbuildWorksheetSteps(definition, result, definition.groups.map((group) => buildWorksheetGroup(group, result.groups)))\n * }\n * ```\n */\nexport function buildWorksheetSteps(\n\tdefinition: QuantitativeDefinition,\n\tresult: QuantitativeResult,\n\tgroups: readonly WorksheetGroup[],\n): readonly Step[] {\n\tconst output: Step[] = []\n\tfor (const group of groups) {\n\t\tfor (const factor of group.factors) {\n\t\t\tif (factor.applied && factor.value !== undefined) {\n\t\t\t\toutput.push(\n\t\t\t\t\tbuildWorksheetStep(\n\t\t\t\t\t\t'factor',\n\t\t\t\t\t\tfactor.id,\n\t\t\t\t\t\tfactor.name,\n\t\t\t\t\t\tfactor.value,\n\t\t\t\t\t\t`${factor.id} = ${factor.value}`,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\toutput.push(\n\t\t\tbuildWorksheetStep(\n\t\t\t\t'group',\n\t\t\t\tgroup.id,\n\t\t\t\tgroup.name,\n\t\t\t\tgroup.value,\n\t\t\t\t`${group.id} = ${group.value}`,\n\t\t\t),\n\t\t)\n\t}\n\toutput.push(\n\t\tbuildWorksheetStep(\n\t\t\t'total',\n\t\t\tdefinition.id,\n\t\t\tdefinition.name,\n\t\t\tresult.value,\n\t\t\t`${definition.aggregation} = ${result.value}`,\n\t\t),\n\t)\n\treturn output\n}\n\n/**\n * Joins a {@link QuantitativeDefinition} and its {@link QuantitativeResult} into\n * a {@link Worksheet} — the rating audit trail.\n *\n * @param definition - The authored quantitative definition\n * @param result - The evaluated quantitative result\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh worksheet\n *\n * @example\n * ```ts\n * import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'\n * import { buildWorksheet } from '@orkestrel/rater'\n *\n * const definition = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)\n * if (result.reasoning === 'quantitative') buildWorksheet(definition, result) // the full audit trail\n * ```\n */\nexport function buildWorksheet(\n\tdefinition: QuantitativeDefinition,\n\tresult: QuantitativeResult,\n\tlabels?: Readonly<Record<string, string>>,\n): Worksheet {\n\tconst groups = definition.groups.map((group) => buildWorksheetGroup(group, result.groups, labels))\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\taggregation: definition.aggregation,\n\t\t...(definition.precision === undefined ? {} : { precision: definition.precision }),\n\t\tvalue: result.value,\n\t\tgroups,\n\t\tsteps: buildWorksheetSteps(definition, result, groups),\n\t\ttrace: result.trace,\n\t\terrors: result.errors,\n\t\tsuccess: result.success,\n\t}\n}\n\n/**\n * Builds a rated {@link LineResult} from a line's evaluated\n * {@link QuantitativeResult}.\n *\n * @param definition - The authored line definition\n * @param result - The evaluated quantitative result\n * @param labels - Optional field-to-label overrides, keyed by dot-joined field\n * @returns A fresh line result\n *\n * @example\n * ```ts\n * import { createQuantitativeDefinition, createQuantitativeReasoner, createReason } from '@orkestrel/reason'\n * import { buildLineDefinition, buildLineResult } from '@orkestrel/rater'\n *\n * const line = buildLineDefinition('base', 'Base', createQuantitativeDefinition('base', 'Base', []))\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, line.rate)\n * if (result.reasoning === 'quantitative') buildLineResult(line, result) // amount present only on a successful worksheet\n * ```\n */\nexport function buildLineResult(\n\tdefinition: LineDefinition,\n\tresult: QuantitativeResult,\n\tlabels?: Readonly<Record<string, string>>,\n): LineResult {\n\treturn {\n\t\tid: definition.id,\n\t\tname: definition.name,\n\t\t...(result.success ? { amount: result.value } : {}),\n\t\tworksheet: buildWorksheet(definition.rate, result, labels),\n\t}\n}\n\n/**\n * Sums defined line amounts.\n *\n * @param lines - The rated lines\n * @returns The sum of every line's `amount`, or `undefined` when no line has one\n *\n * @example\n * ```ts\n * import { createFactorGroup, createQuantitativeDefinition, createQuantitativeReasoner, createReason, createStaticFactor } from '@orkestrel/reason'\n * import { buildLineDefinition, buildLineResult, sumAmounts } from '@orkestrel/rater'\n *\n * const rate = createQuantitativeDefinition('base', 'Base', [createFactorGroup('g', 'sum', [createStaticFactor('flat', 100)])])\n * const line = buildLineDefinition('base', 'Base', rate)\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, rate)\n * if (result.reasoning === 'quantitative') sumAmounts([buildLineResult(line, result)]) // 100\n * ```\n */\nexport function sumAmounts(lines: readonly LineResult[]): number | undefined {\n\tlet total = 0\n\tlet count = 0\n\tfor (const rated of lines) {\n\t\tif (rated.amount !== undefined) {\n\t\t\ttotal += rated.amount\n\t\t\tcount += 1\n\t\t}\n\t}\n\treturn count === 0 ? undefined : total\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tQuantitativeDefinition,\n\tQuantitativeResult,\n\tReasonInterface,\n\tSubject,\n} from '@orkestrel/reason'\nimport type {\n\tLineDefinition,\n\tLineResult,\n\tRaterEventMap,\n\tRaterInterface,\n\tRaterOptions,\n\tRatingDefinition,\n\tRatingResult,\n\tTotalHandler,\n} from './types.js'\nimport { Emitter } from '@orkestrel/emitter'\nimport { arrayOf, isArray, isRecord } from '@orkestrel/contract'\nimport { buildErrorResult, createQuantitativeReasoner, createReason } from '@orkestrel/reason'\nimport { RaterError } from './errors.js'\nimport { buildLineResult, sumAmounts } from './helpers.js'\nimport { isLineDefinition, isRatingDefinition } from './validators.js'\n\n/**\n * Orchestrates rating — owns (or receives) the shared quantitative\n * reasoning engine and projects results into the rating domain vocabulary.\n *\n * @remarks\n * When no `engine` is injected, `Rater` builds and owns its own\n * quantitative-only engine (`bail: false`) — Rater performs no evaluation\n * arithmetic of its own; it only orchestrates and projects. The\n * array-of-lines `rate` overload is declared first so a plain line list\n * resolves to that form. `destroy()` destroys an owned engine, then the\n * emitter last; an injected engine is never destroyed. Afterwards every\n * other method throws {@link RaterError} `'DESTROYED'`.\n *\n * @example\n * ```ts\n * import { Rater } from '@orkestrel/rater'\n *\n * const rater = new Rater()\n * rater.rate([], { id: 'subject-1' }) // { lines: [], success: true }\n * rater.destroy()\n * ```\n */\nexport class Rater implements RaterInterface {\n\treadonly #emitter: Emitter<RaterEventMap>\n\treadonly #engine: ReasonInterface\n\treadonly #owned: boolean\n\treadonly #total: TotalHandler | undefined\n\treadonly #labels: Readonly<Record<string, string>> | undefined\n\t#destroyed = false\n\n\tconstructor(options?: RaterOptions) {\n\t\tthis.#emitter = new Emitter<RaterEventMap>({\n\t\t\t...(options?.on === undefined ? {} : { on: options.on }),\n\t\t\t...(options?.error === undefined ? {} : { error: options.error }),\n\t\t})\n\t\tif (options?.engine === undefined) {\n\t\t\tthis.#engine = createReason({ reasoners: [createQuantitativeReasoner()], bail: false })\n\t\t\tthis.#owned = true\n\t\t} else {\n\t\t\tthis.#engine = options.engine\n\t\t\tthis.#owned = false\n\t\t}\n\t\tthis.#total = options?.total\n\t\tthis.#labels = options?.labels\n\t}\n\n\tget emitter(): EmitterInterface<RaterEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t// Array overload first so a plain line list resolves to that form.\n\trate(lines: readonly LineDefinition[], subject: Subject): RatingResult\n\trate(definition: RatingDefinition, subject: Subject): RatingResult\n\trate(input: readonly LineDefinition[] | RatingDefinition, subject: Subject): RatingResult {\n\t\tthis.#ensureAlive()\n\t\tconst lines = this.#normalize(input)\n\t\tif (!isRecord(subject)) throw new RaterError('MISMATCH', 'Subject must be a record')\n\t\tconst results = lines.map((line) => this.#rateLine(line, subject))\n\t\tconst total = (this.#total ?? sumAmounts)(results)\n\t\tconst success = results.every((entry) => entry.worksheet.success)\n\t\tconst result: RatingResult = {\n\t\t\tlines: results,\n\t\t\t...(total === undefined ? {} : { total }),\n\t\t\tsuccess,\n\t\t}\n\t\tthis.#emitter.emit('rate', subject, result)\n\t\treturn result\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\t\tif (this.#owned) this.#engine.destroy()\n\t\tthis.#emitter.destroy()\n\t}\n\n\t#normalize(input: readonly LineDefinition[] | RatingDefinition): readonly LineDefinition[] {\n\t\tif (arrayOf(isLineDefinition)(input)) return input\n\t\tif (isRatingDefinition(input)) return input.lines\n\t\tthrow new RaterError(\n\t\t\t'DEFINITION',\n\t\t\t'Rating input must be an array of line definitions or a rating definition',\n\t\t)\n\t}\n\n\t#rateLine(line: LineDefinition, subject: Subject): LineResult {\n\t\tconst result = this.#reasonQuantitative(subject, line.rate)\n\t\treturn buildLineResult(line, result, this.#labels)\n\t}\n\n\t// Narrows the engine's tagged ReasonResult union down to a QuantitativeResult\n\t// for a definition the engine is guaranteed (by dispatch-by-reasoning, given\n\t// only the quantitative reasoner is ever registered) to resolve as such — a\n\t// defensive, total fallback rather than an assertion. A nonconforming\n\t// injected engine result missing (or with a non-array) `errors` contributes\n\t// no errors from that side rather than throwing.\n\t#reasonQuantitative(subject: Subject, definition: QuantitativeDefinition): QuantitativeResult {\n\t\tconst result = this.#engine.reason(subject, definition)\n\t\tif (result.reasoning === 'quantitative') return result\n\t\tconst failure = buildErrorResult(\n\t\t\tdefinition,\n\t\t\t`Expected quantitative result, got ${result.reasoning}`,\n\t\t)\n\t\tconst errors = [...(isArray<string>(result.errors) ? result.errors : []), ...failure.errors]\n\t\tif (failure.reasoning === 'quantitative') return { ...failure, errors }\n\t\treturn {\n\t\t\treasoning: 'quantitative',\n\t\t\tvalue: 0,\n\t\t\tgroups: [],\n\t\t\tcount: 0,\n\t\t\tsuccess: false,\n\t\t\ttrace: [],\n\t\t\terrors,\n\t\t}\n\t}\n\n\t#ensureAlive(): void {\n\t\tif (this.#destroyed) throw new RaterError('DESTROYED', 'Rater has been destroyed')\n\t}\n}\n","import type { RaterInterface, RaterOptions } from './types.js'\nimport { Rater } from './Rater.js'\n\n/**\n * Creates a rating orchestrator over the shared quantitative engine, seeded\n * from {@link RaterOptions} and returning a {@link RaterInterface}.\n *\n * @param options - Optional total handler, labels, injected engine, and emitter hooks\n * @returns A {@link RaterInterface}\n *\n * @example Create a rater\n * ```ts\n * import { createRater } from '@orkestrel/rater'\n *\n * const rater = createRater()\n * rater.destroy()\n * ```\n */\nexport function createRater(options?: RaterOptions): RaterInterface {\n\treturn new Rater(options)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAmBA,IAAa,aAAb,cAAgC,MAAM;CACrC;CACA;CAEA,YAAY,MAAsB,SAAiB,SAA6C;EAC/F,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,IAAI,YAAY,KAAA,GAAW,KAAK,UAAU;CAC3C;AACD;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,aAAa,OAAqC;CACjE,QAAA,GAAO,oBAAA,WAAA,CAAW,OAAO,UAAU;AACpC;;;;;;;;;;;;;;;;;;;;;ACFA,IAAa,WAAA,GAAwB,oBAAA,UAAA,CAAU,UAAU,SAAS,OAAO;;;;;;;;;;;;;;;;;;;;;;AAuBzE,SAAgB,iBAAiB,OAAyC;CACzE,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,aAAa,oBAAA;EACb,MAAM,kBAAA;EACN,UAAU,oBAAA;CACX,GACA,CAAC,eAAe,UAAU,CAC3B,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,mBAAmB,OAA2C;CAC7E,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,aAAa,oBAAA;EACb,QAAA,GAAO,oBAAA,QAAA,CAAQ,gBAAgB;EAC/B,UAAU,oBAAA;CACX,GACA,CAAC,eAAe,UAAU,CAC3B,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,WAAW,OAAmC;CAC7D,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,OAAO,kBAAA;EACP,OAAO,oBAAA;EACP,YAAY,kBAAA;EACZ,KAAK,oBAAA;CACN,GACA;EAAC;EAAS;EAAS;EAAc;CAAK,CACvC,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,kBAAkB,OAA0C;CAC3E,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,aAAa,oBAAA;EACb,SAAS,oBAAA;EACT,OAAO,oBAAA;EACP,WAAA,GAAU,oBAAA,QAAA,CAAQ,UAAU;CAC7B,GACA;EAAC;EAAQ;EAAe;CAAO,CAChC,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,iBAAiB,OAAyC;CACzE,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,aAAa,oBAAA;EACb,SAAS,oBAAA;EACT,OAAO,oBAAA;EACP,UAAA,GAAS,oBAAA,QAAA,CAAQ,iBAAiB;CACnC,GACA,CAAC,QAAQ,aAAa,CACvB,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,OAAO,OAA+B;CACrD,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,OAAO;EACP,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,OAAO,oBAAA;EACP,YAAY,oBAAA;CACb,GACA;EAAC;EAAM;EAAQ;CAAY,CAC5B,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,YAAY,OAAoC;CAC/D,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,aAAa,kBAAA;EACb,WAAW,oBAAA;EACX,OAAO,oBAAA;EACP,SAAA,GAAQ,oBAAA,QAAA,CAAQ,gBAAgB;EAChC,QAAA,GAAO,oBAAA,QAAA,CAAQ,MAAM;EACrB,QAAA,GAAO,oBAAA,QAAA,CAAQ,oBAAA,QAAQ;EACvB,SAAA,GAAQ,oBAAA,QAAA,CAAQ,oBAAA,QAAQ;EACxB,SAAS,oBAAA;CACV,GACA,CAAC,WAAW,CACb,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,aAAa,OAAqC;CACjE,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,IAAI,oBAAA;EACJ,MAAM,oBAAA;EACN,QAAQ,oBAAA;EACR,WAAW;CACZ,GACA,CAAC,QAAQ,CACV,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,eAAe,OAAuC;CACrE,QAAA,GAAO,oBAAA,SAAA,CACN;EACC,QAAA,GAAO,oBAAA,QAAA,CAAQ,YAAY;EAC3B,OAAO,oBAAA;EACP,SAAS,oBAAA;CACV,GACA,CAAC,OAAO,CACT,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;;;AC5TA,SAAgB,oBACf,IACA,MACA,MACA,WACiB;CACjB,OAAO;EAAE;EAAI;EAAM;EAAM,GAAG;CAAU;AACvC;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,sBACf,IACA,MACA,OACA,WACmB;CACnB,OAAO;EAAE;EAAI;EAAM;EAAO,GAAG;CAAU;AACxC;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,cACf,OACA,QACA,KACA,QACW;CACX,MAAM,SAAA,GAAQ,kBAAA,YAAA,CAAY,MAAM,KAAK;CACrC,OAAO;EACN,OAAO,MAAM;EACb,GAAI,SAAS,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,OAAO,OAAO;EAChE,YAAY,MAAM;EAClB,UAAU,MAAM;EAChB;EACA,GAAI,QAAQ,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI;CACpC;AACD;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,kBACf,QACA,SACA,QACsB;CACtB,QAAQ,UAAU,CAAC,EAAA,CAAG,KAAK,OAAO,UAAU;EAC3C,MAAM,SAAS,UAAU;EACzB,OAAO,cAAc,OAAO,QAAQ,QAAQ,QAAQ,KAAK,MAAM;CAChE,CAAC;AACF;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,qBACf,YACA,SACA,QACkB;CAClB,MAAM,SAAS,QAAQ,MAAM,UAAU,MAAM,OAAO,WAAW,EAAE;CACjE,OAAO;EACN,IAAI,WAAW;EACf,MAAM,WAAW;EACjB,GAAI,WAAW,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,WAAW,YAAY;EACtF,SAAS,QAAQ,WAAW;EAC5B,GAAI,QAAQ,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,OAAO,MAAM;EAC7D,UAAU,kBAAkB,WAAW,QAAQ,QAAQ,QAAQ,MAAM;CACtE;AACD;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,oBACf,YACA,SACA,QACiB;CACjB,MAAM,SAAS,QAAQ,MAAM,UAAU,MAAM,OAAO,WAAW,EAAE;CACjE,OAAO;EACN,IAAI,WAAW;EACf,MAAM,WAAW;EACjB,GAAI,WAAW,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,WAAW,YAAY;EACtF,SAAS,QAAQ,WAAW;EAC5B,OAAO,QAAQ,SAAS;EACxB,SAAS,WAAW,QAAQ,KAAK,WAChC,qBAAqB,QAAQ,QAAQ,WAAW,CAAC,GAAG,MAAM,CAC3D;CACD;AACD;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,mBACf,OACA,IACA,MACA,OACA,YACO;CACP,OAAO;EACN;EACA,GAAI,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,GAAG;EACjC,GAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK;EACrC;EACA;CACD;AACD;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,oBACf,YACA,QACA,QACkB;CAClB,MAAM,SAAiB,CAAC;CACxB,KAAK,MAAM,SAAS,QAAQ;EAC3B,KAAK,MAAM,UAAU,MAAM,SAC1B,IAAI,OAAO,WAAW,OAAO,UAAU,KAAA,GACtC,OAAO,KACN,mBACC,UACA,OAAO,IACP,OAAO,MACP,OAAO,OACP,GAAG,OAAO,GAAG,KAAK,OAAO,OAC1B,CACD;EAGF,OAAO,KACN,mBACC,SACA,MAAM,IACN,MAAM,MACN,MAAM,OACN,GAAG,MAAM,GAAG,KAAK,MAAM,OACxB,CACD;CACD;CACA,OAAO,KACN,mBACC,SACA,WAAW,IACX,WAAW,MACX,OAAO,OACP,GAAG,WAAW,YAAY,KAAK,OAAO,OACvC,CACD;CACA,OAAO;AACR;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,eACf,YACA,QACA,QACY;CACZ,MAAM,SAAS,WAAW,OAAO,KAAK,UAAU,oBAAoB,OAAO,OAAO,QAAQ,MAAM,CAAC;CACjG,OAAO;EACN,IAAI,WAAW;EACf,MAAM,WAAW;EACjB,aAAa,WAAW;EACxB,GAAI,WAAW,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW,WAAW,UAAU;EAChF,OAAO,OAAO;EACd;EACA,OAAO,oBAAoB,YAAY,QAAQ,MAAM;EACrD,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,SAAS,OAAO;CACjB;AACD;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,gBACf,YACA,QACA,QACa;CACb,OAAO;EACN,IAAI,WAAW;EACf,MAAM,WAAW;EACjB,GAAI,OAAO,UAAU,EAAE,QAAQ,OAAO,MAAM,IAAI,CAAC;EACjD,WAAW,eAAe,WAAW,MAAM,QAAQ,MAAM;CAC1D;AACD;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,WAAW,OAAkD;CAC5E,IAAI,QAAQ;CACZ,IAAI,QAAQ;CACZ,KAAK,MAAM,SAAS,OACnB,IAAI,MAAM,WAAW,KAAA,GAAW;EAC/B,SAAS,MAAM;EACf,SAAS;CACV;CAED,OAAO,UAAU,IAAI,KAAA,IAAY;AAClC;;;;;;;;;;;;;;;;;;;;;;;;;ACxWA,IAAa,QAAb,MAA6C;CAC5C;CACA;CACA;CACA;CACA;CACA,aAAa;CAEb,YAAY,SAAwB;EACnC,KAAK,WAAW,IAAI,mBAAA,QAAuB;GAC1C,GAAI,SAAS,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI,QAAQ,GAAG;GACtD,GAAI,SAAS,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,QAAQ,MAAM;EAChE,CAAC;EACD,IAAI,SAAS,WAAW,KAAA,GAAW;GAClC,KAAK,WAAA,GAAU,kBAAA,aAAA,CAAa;IAAE,WAAW,EAAA,GAAC,kBAAA,2BAAA,CAA2B,CAAC;IAAG,MAAM;GAAM,CAAC;GACtF,KAAK,SAAS;EACf,OAAO;GACN,KAAK,UAAU,QAAQ;GACvB,KAAK,SAAS;EACf;EACA,KAAK,SAAS,SAAS;EACvB,KAAK,UAAU,SAAS;CACzB;CAEA,IAAI,UAA2C;EAC9C,OAAO,KAAK;CACb;CAKA,KAAK,OAAqD,SAAgC;EACzF,KAAK,aAAa;EAClB,MAAM,QAAQ,KAAK,WAAW,KAAK;EACnC,IAAI,EAAA,GAAC,oBAAA,SAAA,CAAS,OAAO,GAAG,MAAM,IAAI,WAAW,YAAY,0BAA0B;EACnF,MAAM,UAAU,MAAM,KAAK,SAAS,KAAK,UAAU,MAAM,OAAO,CAAC;EACjE,MAAM,SAAS,KAAK,UAAU,WAAA,CAAY,OAAO;EACjD,MAAM,UAAU,QAAQ,OAAO,UAAU,MAAM,UAAU,OAAO;EAChE,MAAM,SAAuB;GAC5B,OAAO;GACP,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;GACvC;EACD;EACA,KAAK,SAAS,KAAK,QAAQ,SAAS,MAAM;EAC1C,OAAO;CACR;CAEA,UAAgB;EACf,IAAI,KAAK,YAAY;EACrB,KAAK,aAAa;EAClB,IAAI,KAAK,QAAQ,KAAK,QAAQ,QAAQ;EACtC,KAAK,SAAS,QAAQ;CACvB;CAEA,WAAW,OAAgF;EAC1F,KAAA,GAAI,oBAAA,QAAA,CAAQ,gBAAgB,CAAC,CAAC,KAAK,GAAG,OAAO;EAC7C,IAAI,mBAAmB,KAAK,GAAG,OAAO,MAAM;EAC5C,MAAM,IAAI,WACT,cACA,0EACD;CACD;CAEA,UAAU,MAAsB,SAA8B;EAE7D,OAAO,gBAAgB,MADR,KAAK,oBAAoB,SAAS,KAAK,IACzB,GAAQ,KAAK,OAAO;CAClD;CAQA,oBAAoB,SAAkB,YAAwD;EAC7F,MAAM,SAAS,KAAK,QAAQ,OAAO,SAAS,UAAU;EACtD,IAAI,OAAO,cAAc,gBAAgB,OAAO;EAChD,MAAM,WAAA,GAAU,kBAAA,iBAAA,CACf,YACA,qCAAqC,OAAO,WAC7C;EACA,MAAM,SAAS,CAAC,IAAA,GAAI,oBAAA,QAAA,CAAgB,OAAO,MAAM,IAAI,OAAO,SAAS,CAAC,GAAI,GAAG,QAAQ,MAAM;EAC3F,IAAI,QAAQ,cAAc,gBAAgB,OAAO;GAAE,GAAG;GAAS;EAAO;EACtE,OAAO;GACN,WAAW;GACX,OAAO;GACP,QAAQ,CAAC;GACT,OAAO;GACP,SAAS;GACT,OAAO,CAAC;GACR;EACD;CACD;CAEA,eAAqB;EACpB,IAAI,KAAK,YAAY,MAAM,IAAI,WAAW,aAAa,0BAA0B;CAClF;AACD;;;;;;;;;;;;;;;;;;AC7HA,SAAgB,YAAY,SAAwC;CACnE,OAAO,IAAI,MAAM,OAAO;AACzB"}
|