@orkestrel/rater 0.0.8 → 0.0.10

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.
@@ -118,9 +118,265 @@ function isRatingDefinition(value) {
118
118
  metadata: _orkestrel_contract.isJSONValue
119
119
  }, ["description", "metadata"])(value);
120
120
  }
121
+ /**
122
+ * Determine whether a value is an open result-side {@link Evidence} object.
123
+ *
124
+ * @remarks
125
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
126
+ * refused. `expected` and `actual` are `unknown`, so this guard does not read or
127
+ * check them; either member may also be absent because `objectOf` reads declared
128
+ * members rather than requiring or enumerating own keys.
129
+ *
130
+ * @param value - The value to test
131
+ * @returns `true` when every checked `Evidence` member conforms
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * import { isEvidence } from '@orkestrel/rater'
136
+ *
137
+ * isEvidence({ field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }) // true
138
+ * ```
139
+ */
140
+ function isEvidence(value) {
141
+ return (0, _orkestrel_contract.objectOf)({
142
+ field: _orkestrel_reason.isFieldPath,
143
+ label: _orkestrel_contract.isString,
144
+ comparison: _orkestrel_reason.isComparison,
145
+ met: _orkestrel_contract.isBoolean
146
+ }, [
147
+ "field",
148
+ "label",
149
+ "comparison",
150
+ "met"
151
+ ])(value);
152
+ }
153
+ /**
154
+ * Determine whether a value is an open {@link WorksheetFactor} result object.
155
+ *
156
+ * @remarks
157
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
158
+ * refused. Optional members may be absent or read as `undefined`.
159
+ *
160
+ * @param value - The value to test
161
+ * @returns `true` when every published worksheet-factor member conforms
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * import { isWorksheetFactor } from '@orkestrel/rater'
166
+ *
167
+ * isWorksheetFactor({ id: 'base', applied: true, value: 100, evidence: [] }) // true
168
+ * ```
169
+ */
170
+ function isWorksheetFactor(value) {
171
+ return (0, _orkestrel_contract.objectOf)({
172
+ id: _orkestrel_contract.isString,
173
+ name: _orkestrel_contract.isString,
174
+ description: _orkestrel_contract.isString,
175
+ applied: _orkestrel_contract.isBoolean,
176
+ value: _orkestrel_contract.isNumber,
177
+ evidence: (0, _orkestrel_contract.arrayOf)(isEvidence)
178
+ }, [
179
+ "name",
180
+ "description",
181
+ "value"
182
+ ])(value);
183
+ }
184
+ /**
185
+ * Determine whether a value is an open {@link WorksheetGroup} result object.
186
+ *
187
+ * @remarks
188
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
189
+ * refused. Optional members may be absent or read as `undefined`.
190
+ *
191
+ * @param value - The value to test
192
+ * @returns `true` when every published worksheet-group member conforms
193
+ *
194
+ * @example
195
+ * ```ts
196
+ * import { isWorksheetGroup } from '@orkestrel/rater'
197
+ *
198
+ * isWorksheetGroup({ id: 'charges', applied: true, value: 100, factors: [] }) // true
199
+ * ```
200
+ */
201
+ function isWorksheetGroup(value) {
202
+ return (0, _orkestrel_contract.objectOf)({
203
+ id: _orkestrel_contract.isString,
204
+ name: _orkestrel_contract.isString,
205
+ description: _orkestrel_contract.isString,
206
+ applied: _orkestrel_contract.isBoolean,
207
+ value: _orkestrel_contract.isNumber,
208
+ factors: (0, _orkestrel_contract.arrayOf)(isWorksheetFactor)
209
+ }, ["name", "description"])(value);
210
+ }
211
+ /**
212
+ * Determine whether a value is an open {@link Step} result object.
213
+ *
214
+ * @remarks
215
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
216
+ * refused. Optional members may be absent or read as `undefined`.
217
+ *
218
+ * @param value - The value to test
219
+ * @returns `true` when every published worksheet-step member conforms
220
+ *
221
+ * @example
222
+ * ```ts
223
+ * import { isStep } from '@orkestrel/rater'
224
+ *
225
+ * isStep({ stage: 'total', value: 100 }) // true
226
+ * ```
227
+ */
228
+ function isStep(value) {
229
+ return (0, _orkestrel_contract.objectOf)({
230
+ stage: isStage,
231
+ id: _orkestrel_contract.isString,
232
+ name: _orkestrel_contract.isString,
233
+ value: _orkestrel_contract.isNumber,
234
+ expression: _orkestrel_contract.isString
235
+ }, [
236
+ "id",
237
+ "name",
238
+ "expression"
239
+ ])(value);
240
+ }
241
+ /**
242
+ * Determine whether a value is an open {@link Worksheet} result object.
243
+ *
244
+ * @remarks
245
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
246
+ * refused. Optional members may be absent or read as `undefined`.
247
+ *
248
+ * @param value - The value to test
249
+ * @returns `true` when every published worksheet member conforms
250
+ *
251
+ * @example
252
+ * ```ts
253
+ * import { isWorksheet } from '@orkestrel/rater'
254
+ *
255
+ * isWorksheet({ id: 'quote', name: 'Quote', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: true }) // true
256
+ * ```
257
+ */
258
+ function isWorksheet(value) {
259
+ return (0, _orkestrel_contract.objectOf)({
260
+ id: _orkestrel_contract.isString,
261
+ name: _orkestrel_contract.isString,
262
+ aggregation: _orkestrel_reason.isAggregation,
263
+ precision: _orkestrel_contract.isNumber,
264
+ value: _orkestrel_contract.isNumber,
265
+ groups: (0, _orkestrel_contract.arrayOf)(isWorksheetGroup),
266
+ steps: (0, _orkestrel_contract.arrayOf)(isStep),
267
+ trace: (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString),
268
+ errors: (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString),
269
+ success: _orkestrel_contract.isBoolean
270
+ }, ["precision"])(value);
271
+ }
272
+ /**
273
+ * Determine whether a value is an open {@link LineResult} object.
274
+ *
275
+ * @remarks
276
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
277
+ * refused. `amount` may be absent or read as `undefined`; when present it stays
278
+ * a plain JavaScript `number` without a finite or range refinement.
279
+ *
280
+ * @param value - The value to test
281
+ * @returns `true` when every published line-result member conforms
282
+ *
283
+ * @example
284
+ * ```ts
285
+ * import { isLineResult } from '@orkestrel/rater'
286
+ *
287
+ * isLineResult({ id: 'base', name: 'Base', worksheet: { id: 'base', name: 'Base', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: false }, success: false }) // true
288
+ * ```
289
+ */
290
+ function isLineResult(value) {
291
+ return (0, _orkestrel_contract.objectOf)({
292
+ id: _orkestrel_contract.isString,
293
+ name: _orkestrel_contract.isString,
294
+ amount: _orkestrel_contract.isNumber,
295
+ worksheet: isWorksheet,
296
+ success: _orkestrel_contract.isBoolean
297
+ }, ["amount"])(value);
298
+ }
299
+ /**
300
+ * Determine whether a value is an open {@link RatingResult} object.
301
+ *
302
+ * @remarks
303
+ * Use this guard for a result returned by a borrowed `RaterInterface`. Unknown
304
+ * members, prototypes, and class instances are admitted. Arrays are refused.
305
+ * `total` may be absent or read as `undefined`; when present it stays a plain
306
+ * JavaScript `number` without a finite or range refinement.
307
+ *
308
+ * @param value - The value to test
309
+ * @returns `true` when every published rating-result member conforms
310
+ *
311
+ * @example
312
+ * ```ts
313
+ * import { isRatingResult } from '@orkestrel/rater'
314
+ *
315
+ * isRatingResult({ lines: [], success: true }) // true
316
+ * ```
317
+ */
318
+ function isRatingResult(value) {
319
+ return (0, _orkestrel_contract.objectOf)({
320
+ lines: (0, _orkestrel_contract.arrayOf)(isLineResult),
321
+ total: _orkestrel_contract.isNumber,
322
+ success: _orkestrel_contract.isBoolean
323
+ }, ["total"])(value);
324
+ }
121
325
  //#endregion
122
326
  //#region src/core/helpers.ts
123
327
  /**
328
+ * Build a {@link LineDefinition}.
329
+ *
330
+ * @param id - The line id
331
+ * @param name - The display name
332
+ * @param rate - The line's quantitative rating definition
333
+ * @param overrides - Optional {@link LineDefinition} fields merged over the defaults
334
+ * @returns A fresh line definition
335
+ *
336
+ * @example
337
+ * ```ts
338
+ * import { quantitativeDefinition } from '@orkestrel/reason'
339
+ * import { lineDefinition } from '@orkestrel/rater'
340
+ *
341
+ * lineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', []))
342
+ * ```
343
+ */
344
+ function lineDefinition(id, name, rate, overrides) {
345
+ return {
346
+ id,
347
+ name,
348
+ rate,
349
+ ...overrides
350
+ };
351
+ }
352
+ /**
353
+ * Build a {@link RatingDefinition}.
354
+ *
355
+ * @param id - The rating id
356
+ * @param name - The display name
357
+ * @param lines - The rating's ordered lines
358
+ * @param overrides - Optional {@link RatingDefinition} fields merged over the defaults
359
+ * @returns A fresh rating definition
360
+ *
361
+ * @example
362
+ * ```ts
363
+ * import { lineDefinition, ratingDefinition } from '@orkestrel/rater'
364
+ * import { quantitativeDefinition } from '@orkestrel/reason'
365
+ *
366
+ * ratingDefinition('r1', 'Rating', [
367
+ * lineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', [])),
368
+ * ])
369
+ * ```
370
+ */
371
+ function ratingDefinition(id, name, lines, overrides) {
372
+ return {
373
+ id,
374
+ name,
375
+ lines,
376
+ ...overrides
377
+ };
378
+ }
379
+ /**
124
380
  * Build an {@link Evidence} row from an evaluated {@link Check}.
125
381
  *
126
382
  * @param check - The authored check
@@ -496,68 +752,23 @@ var Rater = class {
496
752
  function createRater(options) {
497
753
  return new Rater(options);
498
754
  }
499
- /**
500
- * Build a {@link LineDefinition}.
501
- *
502
- * @param id - The line id
503
- * @param name - The display name
504
- * @param rate - The line's quantitative rating definition
505
- * @param overrides - Optional {@link LineDefinition} fields merged over the defaults
506
- * @returns A fresh line definition
507
- *
508
- * @example
509
- * ```ts
510
- * import { quantitativeDefinition } from '@orkestrel/reason'
511
- * import { lineDefinition } from '@orkestrel/rater'
512
- *
513
- * lineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', []))
514
- * ```
515
- */
516
- function lineDefinition(id, name, rate, overrides) {
517
- return {
518
- id,
519
- name,
520
- rate,
521
- ...overrides
522
- };
523
- }
524
- /**
525
- * Build a {@link RatingDefinition}.
526
- *
527
- * @param id - The rating id
528
- * @param name - The display name
529
- * @param lines - The rating's ordered lines
530
- * @param overrides - Optional {@link RatingDefinition} fields merged over the defaults
531
- * @returns A fresh rating definition
532
- *
533
- * @example
534
- * ```ts
535
- * import { lineDefinition, ratingDefinition } from '@orkestrel/rater'
536
- * import { quantitativeDefinition } from '@orkestrel/reason'
537
- *
538
- * ratingDefinition('r1', 'Rating', [
539
- * lineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', [])),
540
- * ])
541
- * ```
542
- */
543
- function ratingDefinition(id, name, lines, overrides) {
544
- return {
545
- id,
546
- name,
547
- lines,
548
- ...overrides
549
- };
550
- }
551
755
  //#endregion
552
756
  exports.Rater = Rater;
553
757
  exports.RaterError = RaterError;
554
758
  exports.checkEvidence = checkEvidence;
555
759
  exports.createRater = createRater;
556
760
  exports.evidenceCheck = evidenceCheck;
761
+ exports.isEvidence = isEvidence;
557
762
  exports.isLineDefinition = isLineDefinition;
763
+ exports.isLineResult = isLineResult;
558
764
  exports.isRaterError = isRaterError;
559
765
  exports.isRatingDefinition = isRatingDefinition;
766
+ exports.isRatingResult = isRatingResult;
560
767
  exports.isStage = isStage;
768
+ exports.isStep = isStep;
769
+ exports.isWorksheet = isWorksheet;
770
+ exports.isWorksheetFactor = isWorksheetFactor;
771
+ exports.isWorksheetGroup = isWorksheetGroup;
561
772
  exports.lineDefinition = lineDefinition;
562
773
  exports.ratedLine = ratedLine;
563
774
  exports.ratingDefinition = ratingDefinition;
@@ -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 * 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 * Narrow a caught value to a {@link RaterError}.\n *\n * @param value - The caught value to test\n * @returns `true` when `value` is a `RaterError`\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 { LineDefinition, RatingDefinition, Stage } from './types.js'\nimport { arrayOf, isJSONValue, isString, literalOf, recordOf } from '@orkestrel/contract'\nimport { isQuantitativeDefinition } from '@orkestrel/reason'\n\n/**\n * Determine whether a value is a {@link Stage} literal.\n *\n * @param value - The value to test\n * @returns `true` when `value` is `'factor'`, `'group'`, or `'total'`\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 * Determine whether a value is an exact {@link LineDefinition} record.\n *\n * @remarks\n * Total guard (AGENTS §14): adversarial input (cycles, hostile prototypes)\n * returns `false`, never throws. The record shape is EXACT — an extra key fails.\n *\n * @param value - The value to test\n * @returns `true` when `value` is a `LineDefinition`\n *\n * @example\n * ```ts\n * import { quantitativeDefinition } from '@orkestrel/reason'\n * import { isLineDefinition } from '@orkestrel/rater'\n *\n * isLineDefinition({ id: 'base', name: 'Base', rate: quantitativeDefinition('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 * Determine whether a value is an exact {@link RatingDefinition} record.\n *\n * @remarks\n * Total guard (AGENTS §14): adversarial input (cycles, hostile prototypes)\n * returns `false`, never throws. The record shape is EXACT — an extra key fails.\n *\n * @param value - The value to test\n * @returns `true` when `value` is a `RatingDefinition`\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","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\tStage,\n\tStep,\n\tWorksheet,\n\tWorksheetFactor,\n\tWorksheetGroup,\n} from './types.js'\nimport { formatField } from '@orkestrel/reason'\n\n/**\n * Build 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 - Whether the check was met (absent when 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 { check } from '@orkestrel/reason'\n * import { evidenceCheck } from '@orkestrel/rater'\n *\n * const entry = check('age', 'above', 18)\n * evidenceCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }\n * ```\n */\nexport function evidenceCheck(\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 * Build evidence rows from a quantitative factor's authored checks and\n * evaluated check results.\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 { check } from '@orkestrel/reason'\n * import { checkEvidence } from '@orkestrel/rater'\n *\n * const checks = [check('age', 'above', 18)]\n * checkEvidence(checks, [{ field: 'age', met: true, actual: 25 }])\n * ```\n */\nexport function checkEvidence(\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 evidenceCheck(check, result?.actual, result?.met, labels)\n\t})\n}\n\n/**\n * Join 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 { fieldFactor } from '@orkestrel/reason'\n * import { worksheetFactor } from '@orkestrel/rater'\n *\n * const factor = fieldFactor('seats', 'seats')\n * worksheetFactor(factor, [{ id: 'seats', applied: true, value: 10 }])\n * ```\n */\nexport function worksheetFactor(\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: checkEvidence(definition.checks, result?.checks, labels),\n\t}\n}\n\n/**\n * Join 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 { factorGroup, staticFactor } from '@orkestrel/reason'\n * import { worksheetGroup } from '@orkestrel/rater'\n *\n * const group = factorGroup('charge', 'sum', [staticFactor('base', 100)])\n * worksheetGroup(group, [{ id: 'charge', applied: true, value: 100, factors: [] }])\n * ```\n */\nexport function worksheetGroup(\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\tworksheetFactor(factor, result?.factors ?? [], labels),\n\t\t),\n\t}\n}\n\n/**\n * Build 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 { worksheetStep } from '@orkestrel/rater'\n *\n * worksheetStep('total', 'quote', 'Quote', 110, 'sum = 110')\n * // { stage: 'total', id: 'quote', name: 'Quote', value: 110, expression: 'sum = 110' }\n * ```\n */\nexport function worksheetStep(\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 * Build 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 { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'\n * import { worksheetGroup, worksheetSteps } from '@orkestrel/rater'\n *\n * const definition = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)\n * if (result.reasoning === 'quantitative') {\n * \tworksheetSteps(definition, result, definition.groups.map((group) => worksheetGroup(group, result.groups)))\n * }\n * ```\n */\nexport function worksheetSteps(\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\tworksheetStep(\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\tworksheetStep('group', group.id, group.name, group.value, `${group.id} = ${group.value}`),\n\t\t)\n\t}\n\toutput.push(\n\t\tworksheetStep(\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 * Join 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 { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'\n * import { resultsWorksheet } from '@orkestrel/rater'\n *\n * const definition = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)\n * if (result.reasoning === 'quantitative') resultsWorksheet(definition, result) // the full audit trail\n * ```\n */\nexport function resultsWorksheet(\n\tdefinition: QuantitativeDefinition,\n\tresult: QuantitativeResult,\n\tlabels?: Readonly<Record<string, string>>,\n): Worksheet {\n\tconst groups = definition.groups.map((group) => worksheetGroup(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: worksheetSteps(definition, result, groups),\n\t\ttrace: result.trace,\n\t\terrors: result.errors,\n\t\tsuccess: result.success,\n\t}\n}\n\n/**\n * Build 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 { createQuantitativeReasoner, createReason, quantitativeDefinition } from '@orkestrel/reason'\n * import { lineDefinition, ratedLine } from '@orkestrel/rater'\n *\n * const line = lineDefinition('base', 'Base', quantitativeDefinition('base', 'Base', []))\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, line.rate)\n * if (result.reasoning === 'quantitative') ratedLine(line, result) // amount present only when success\n * ```\n */\nexport function ratedLine(\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: resultsWorksheet(definition.rate, result, labels),\n\t\tsuccess: result.success,\n\t}\n}\n\n/**\n * Sum 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 { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'\n * import { lineDefinition, ratedLine, sumAmounts } from '@orkestrel/rater'\n *\n * const rate = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])\n * const line = lineDefinition('base', 'Base', rate)\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, rate)\n * if (result.reasoning === 'quantitative') sumAmounts([ratedLine(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 } from '@orkestrel/contract'\nimport {\n\tbuildErrorResult,\n\tcreateQuantitativeReasoner,\n\tcreateReason,\n\tisSubject,\n} from '@orkestrel/reason'\nimport { RaterError } from './errors.js'\nimport { ratedLine, sumAmounts } from './helpers.js'\nimport { isLineDefinition, isRatingDefinition } from './validators.js'\n\n/**\n * The rating orchestrator — 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 (AGENTS §9.2). `destroy()`\n * destroys an OWNED engine, then the emitter LAST (AGENTS §13); an INJECTED\n * engine is never destroyed. Afterwards every other method throws\n * {@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 (AGENTS §9.2) 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 (!isSubject(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.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 ratedLine(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 { QuantitativeDefinition } from '@orkestrel/reason'\nimport type { LineDefinition, RatingDefinition, RaterInterface, RaterOptions } from './types.js'\nimport { Rater } from './Rater.js'\n\n/**\n * Create 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\n/**\n * Build 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 { quantitativeDefinition } from '@orkestrel/reason'\n * import { lineDefinition } from '@orkestrel/rater'\n *\n * lineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', []))\n * ```\n */\nexport function lineDefinition(\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 * Build 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 { lineDefinition, ratingDefinition } from '@orkestrel/rater'\n * import { quantitativeDefinition } from '@orkestrel/reason'\n *\n * ratingDefinition('r1', 'Rating', [\n * \tlineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', [])),\n * ])\n * ```\n */\nexport function ratingDefinition(\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"],"mappings":"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"}
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 * 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 * Narrow a caught value to a {@link RaterError}.\n *\n * @param value - The caught value to test\n * @returns `true` when `value` is a `RaterError`\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 * Determine whether a value is a {@link Stage} literal.\n *\n * @param value - The value to test\n * @returns `true` when `value` is `'factor'`, `'group'`, or `'total'`\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 * Determine whether a value is an exact {@link LineDefinition} record.\n *\n * @remarks\n * Total guard (AGENTS §14): adversarial input (cycles, hostile prototypes)\n * returns `false`, never throws. The record shape is EXACT — an extra key fails.\n *\n * @param value - The value to test\n * @returns `true` when `value` is a `LineDefinition`\n *\n * @example\n * ```ts\n * import { quantitativeDefinition } from '@orkestrel/reason'\n * import { isLineDefinition } from '@orkestrel/rater'\n *\n * isLineDefinition({ id: 'base', name: 'Base', rate: quantitativeDefinition('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 * Determine whether a value is an exact {@link RatingDefinition} record.\n *\n * @remarks\n * Total guard (AGENTS §14): adversarial input (cycles, hostile prototypes)\n * returns `false`, never throws. The record shape is EXACT — an extra key fails.\n *\n * @param value - The value to test\n * @returns `true` when `value` is a `RatingDefinition`\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 * Determine 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` when every checked `Evidence` member conforms\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 * Determine 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` when every published worksheet-factor member conforms\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 * Determine 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` when every published worksheet-group member conforms\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 * Determine 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` when every published worksheet-step member conforms\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 * Determine 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` when every published worksheet member conforms\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 * Determine 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.\n *\n * @param value - The value to test\n * @returns `true` when every published line-result member conforms\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 }, 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\tsuccess: isBoolean,\n\t\t},\n\t\t['amount'],\n\t)(value)\n}\n\n/**\n * Determine 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` when every published rating-result member conforms\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 * Build 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 { quantitativeDefinition } from '@orkestrel/reason'\n * import { lineDefinition } from '@orkestrel/rater'\n *\n * lineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', []))\n * ```\n */\nexport function lineDefinition(\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 * Build 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 { lineDefinition, ratingDefinition } from '@orkestrel/rater'\n * import { quantitativeDefinition } from '@orkestrel/reason'\n *\n * ratingDefinition('r1', 'Rating', [\n * \tlineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', [])),\n * ])\n * ```\n */\nexport function ratingDefinition(\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 * Build 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 - Whether the check was met (absent when 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 { check } from '@orkestrel/reason'\n * import { evidenceCheck } from '@orkestrel/rater'\n *\n * const entry = check('age', 'above', 18)\n * evidenceCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }\n * ```\n */\nexport function evidenceCheck(\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 * Build evidence rows from a quantitative factor's authored checks and\n * evaluated check results.\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 { check } from '@orkestrel/reason'\n * import { checkEvidence } from '@orkestrel/rater'\n *\n * const checks = [check('age', 'above', 18)]\n * checkEvidence(checks, [{ field: 'age', met: true, actual: 25 }])\n * ```\n */\nexport function checkEvidence(\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 evidenceCheck(check, result?.actual, result?.met, labels)\n\t})\n}\n\n/**\n * Join 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 { fieldFactor } from '@orkestrel/reason'\n * import { worksheetFactor } from '@orkestrel/rater'\n *\n * const factor = fieldFactor('seats', 'seats')\n * worksheetFactor(factor, [{ id: 'seats', applied: true, value: 10 }])\n * ```\n */\nexport function worksheetFactor(\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: checkEvidence(definition.checks, result?.checks, labels),\n\t}\n}\n\n/**\n * Join 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 { factorGroup, staticFactor } from '@orkestrel/reason'\n * import { worksheetGroup } from '@orkestrel/rater'\n *\n * const group = factorGroup('charge', 'sum', [staticFactor('base', 100)])\n * worksheetGroup(group, [{ id: 'charge', applied: true, value: 100, factors: [] }])\n * ```\n */\nexport function worksheetGroup(\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\tworksheetFactor(factor, result?.factors ?? [], labels),\n\t\t),\n\t}\n}\n\n/**\n * Build 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 { worksheetStep } from '@orkestrel/rater'\n *\n * worksheetStep('total', 'quote', 'Quote', 110, 'sum = 110')\n * // { stage: 'total', id: 'quote', name: 'Quote', value: 110, expression: 'sum = 110' }\n * ```\n */\nexport function worksheetStep(\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 * Build 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 { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'\n * import { worksheetGroup, worksheetSteps } from '@orkestrel/rater'\n *\n * const definition = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)\n * if (result.reasoning === 'quantitative') {\n * \tworksheetSteps(definition, result, definition.groups.map((group) => worksheetGroup(group, result.groups)))\n * }\n * ```\n */\nexport function worksheetSteps(\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\tworksheetStep(\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\tworksheetStep('group', group.id, group.name, group.value, `${group.id} = ${group.value}`),\n\t\t)\n\t}\n\toutput.push(\n\t\tworksheetStep(\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 * Join 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 { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'\n * import { resultsWorksheet } from '@orkestrel/rater'\n *\n * const definition = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)\n * if (result.reasoning === 'quantitative') resultsWorksheet(definition, result) // the full audit trail\n * ```\n */\nexport function resultsWorksheet(\n\tdefinition: QuantitativeDefinition,\n\tresult: QuantitativeResult,\n\tlabels?: Readonly<Record<string, string>>,\n): Worksheet {\n\tconst groups = definition.groups.map((group) => worksheetGroup(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: worksheetSteps(definition, result, groups),\n\t\ttrace: result.trace,\n\t\terrors: result.errors,\n\t\tsuccess: result.success,\n\t}\n}\n\n/**\n * Build 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 { createQuantitativeReasoner, createReason, quantitativeDefinition } from '@orkestrel/reason'\n * import { lineDefinition, ratedLine } from '@orkestrel/rater'\n *\n * const line = lineDefinition('base', 'Base', quantitativeDefinition('base', 'Base', []))\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, line.rate)\n * if (result.reasoning === 'quantitative') ratedLine(line, result) // amount present only when success\n * ```\n */\nexport function ratedLine(\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: resultsWorksheet(definition.rate, result, labels),\n\t\tsuccess: result.success,\n\t}\n}\n\n/**\n * Sum 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 { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'\n * import { lineDefinition, ratedLine, sumAmounts } from '@orkestrel/rater'\n *\n * const rate = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])\n * const line = lineDefinition('base', 'Base', rate)\n * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, rate)\n * if (result.reasoning === 'quantitative') sumAmounts([ratedLine(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 } from '@orkestrel/contract'\nimport {\n\tbuildErrorResult,\n\tcreateQuantitativeReasoner,\n\tcreateReason,\n\tisSubject,\n} from '@orkestrel/reason'\nimport { RaterError } from './errors.js'\nimport { ratedLine, sumAmounts } from './helpers.js'\nimport { isLineDefinition, isRatingDefinition } from './validators.js'\n\n/**\n * The rating orchestrator — 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 (AGENTS §9.2). `destroy()`\n * destroys an OWNED engine, then the emitter LAST (AGENTS §13); an INJECTED\n * engine is never destroyed. Afterwards every other method throws\n * {@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 (AGENTS §9.2) 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 (!isSubject(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.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 ratedLine(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 * Create 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":"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"}
@@ -82,6 +82,27 @@ export declare interface Evidence {
82
82
  */
83
83
  export declare function evidenceCheck(check: Check, actual: unknown, met: boolean | undefined, labels?: Readonly<Record<string, string>>): Evidence;
84
84
 
85
+ /**
86
+ * Determine whether a value is an open result-side {@link Evidence} object.
87
+ *
88
+ * @remarks
89
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
90
+ * refused. `expected` and `actual` are `unknown`, so this guard does not read or
91
+ * check them; either member may also be absent because `objectOf` reads declared
92
+ * members rather than requiring or enumerating own keys.
93
+ *
94
+ * @param value - The value to test
95
+ * @returns `true` when every checked `Evidence` member conforms
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * import { isEvidence } from '@orkestrel/rater'
100
+ *
101
+ * isEvidence({ field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }) // true
102
+ * ```
103
+ */
104
+ export declare function isEvidence(value: unknown): value is Evidence;
105
+
85
106
  /**
86
107
  * Determine whether a value is an exact {@link LineDefinition} record.
87
108
  *
@@ -102,6 +123,26 @@ export declare function evidenceCheck(check: Check, actual: unknown, met: boolea
102
123
  */
103
124
  export declare function isLineDefinition(value: unknown): value is LineDefinition;
104
125
 
126
+ /**
127
+ * Determine whether a value is an open {@link LineResult} object.
128
+ *
129
+ * @remarks
130
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
131
+ * refused. `amount` may be absent or read as `undefined`; when present it stays
132
+ * a plain JavaScript `number` without a finite or range refinement.
133
+ *
134
+ * @param value - The value to test
135
+ * @returns `true` when every published line-result member conforms
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * import { isLineResult } from '@orkestrel/rater'
140
+ *
141
+ * isLineResult({ id: 'base', name: 'Base', worksheet: { id: 'base', name: 'Base', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: false }, success: false }) // true
142
+ * ```
143
+ */
144
+ export declare function isLineResult(value: unknown): value is LineResult;
145
+
105
146
  /**
106
147
  * Narrow a caught value to a {@link RaterError}.
107
148
  *
@@ -140,6 +181,27 @@ export declare function isRaterError(value: unknown): value is RaterError;
140
181
  */
141
182
  export declare function isRatingDefinition(value: unknown): value is RatingDefinition;
142
183
 
184
+ /**
185
+ * Determine whether a value is an open {@link RatingResult} object.
186
+ *
187
+ * @remarks
188
+ * Use this guard for a result returned by a borrowed `RaterInterface`. Unknown
189
+ * members, prototypes, and class instances are admitted. Arrays are refused.
190
+ * `total` may be absent or read as `undefined`; when present it stays a plain
191
+ * JavaScript `number` without a finite or range refinement.
192
+ *
193
+ * @param value - The value to test
194
+ * @returns `true` when every published rating-result member conforms
195
+ *
196
+ * @example
197
+ * ```ts
198
+ * import { isRatingResult } from '@orkestrel/rater'
199
+ *
200
+ * isRatingResult({ lines: [], success: true }) // true
201
+ * ```
202
+ */
203
+ export declare function isRatingResult(value: unknown): value is RatingResult;
204
+
143
205
  /**
144
206
  * Determine whether a value is a {@link Stage} literal.
145
207
  *
@@ -156,6 +218,82 @@ export declare function isRatingDefinition(value: unknown): value is RatingDefin
156
218
  */
157
219
  export declare const isStage: Guard<Stage>;
158
220
 
221
+ /**
222
+ * Determine whether a value is an open {@link Step} result object.
223
+ *
224
+ * @remarks
225
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
226
+ * refused. Optional members may be absent or read as `undefined`.
227
+ *
228
+ * @param value - The value to test
229
+ * @returns `true` when every published worksheet-step member conforms
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * import { isStep } from '@orkestrel/rater'
234
+ *
235
+ * isStep({ stage: 'total', value: 100 }) // true
236
+ * ```
237
+ */
238
+ export declare function isStep(value: unknown): value is Step;
239
+
240
+ /**
241
+ * Determine whether a value is an open {@link Worksheet} result object.
242
+ *
243
+ * @remarks
244
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
245
+ * refused. Optional members may be absent or read as `undefined`.
246
+ *
247
+ * @param value - The value to test
248
+ * @returns `true` when every published worksheet member conforms
249
+ *
250
+ * @example
251
+ * ```ts
252
+ * import { isWorksheet } from '@orkestrel/rater'
253
+ *
254
+ * isWorksheet({ id: 'quote', name: 'Quote', aggregation: 'sum', value: 0, groups: [], steps: [], trace: [], errors: [], success: true }) // true
255
+ * ```
256
+ */
257
+ export declare function isWorksheet(value: unknown): value is Worksheet;
258
+
259
+ /**
260
+ * Determine whether a value is an open {@link WorksheetFactor} result object.
261
+ *
262
+ * @remarks
263
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
264
+ * refused. Optional members may be absent or read as `undefined`.
265
+ *
266
+ * @param value - The value to test
267
+ * @returns `true` when every published worksheet-factor member conforms
268
+ *
269
+ * @example
270
+ * ```ts
271
+ * import { isWorksheetFactor } from '@orkestrel/rater'
272
+ *
273
+ * isWorksheetFactor({ id: 'base', applied: true, value: 100, evidence: [] }) // true
274
+ * ```
275
+ */
276
+ export declare function isWorksheetFactor(value: unknown): value is WorksheetFactor;
277
+
278
+ /**
279
+ * Determine whether a value is an open {@link WorksheetGroup} result object.
280
+ *
281
+ * @remarks
282
+ * Unknown members, prototypes, and class instances are admitted. Arrays are
283
+ * refused. Optional members may be absent or read as `undefined`.
284
+ *
285
+ * @param value - The value to test
286
+ * @returns `true` when every published worksheet-group member conforms
287
+ *
288
+ * @example
289
+ * ```ts
290
+ * import { isWorksheetGroup } from '@orkestrel/rater'
291
+ *
292
+ * isWorksheetGroup({ id: 'charges', applied: true, value: 100, factors: [] }) // true
293
+ * ```
294
+ */
295
+ export declare function isWorksheetGroup(value: unknown): value is WorksheetGroup;
296
+
159
297
  /**
160
298
  * One rateable line — a quantitative definition joined to display metadata.
161
299
  *