@orkestrel/rater 0.0.2 → 0.0.4
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/dist/src/core/index.cjs +17 -17
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +38 -38
- package/dist/src/core/index.d.ts +38 -38
- package/dist/src/core/index.js +18 -18
- package/dist/src/core/index.js.map +1 -1
- package/package.json +12 -12
package/dist/src/core/index.cjs
CHANGED
|
@@ -121,24 +121,24 @@ function isRatingDefinition(value) {
|
|
|
121
121
|
//#endregion
|
|
122
122
|
//#region src/core/helpers.ts
|
|
123
123
|
/**
|
|
124
|
-
* Build
|
|
124
|
+
* Build an {@link Evidence} row from an evaluated {@link Check}.
|
|
125
125
|
*
|
|
126
126
|
* @param check - The authored check
|
|
127
127
|
* @param actual - The resolved subject value
|
|
128
128
|
* @param met - Whether the check was met (absent when not yet evaluated)
|
|
129
129
|
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
130
|
-
* @returns A fresh
|
|
130
|
+
* @returns A fresh evidence row
|
|
131
131
|
*
|
|
132
132
|
* @example
|
|
133
133
|
* ```ts
|
|
134
134
|
* import { check } from '@orkestrel/reason'
|
|
135
|
-
* import {
|
|
135
|
+
* import { evidenceCheck } from '@orkestrel/rater'
|
|
136
136
|
*
|
|
137
137
|
* const entry = check('age', 'above', 18)
|
|
138
|
-
*
|
|
138
|
+
* evidenceCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
|
|
139
139
|
* ```
|
|
140
140
|
*/
|
|
141
|
-
function
|
|
141
|
+
function evidenceCheck(check, actual, met, labels) {
|
|
142
142
|
const field = (0, _orkestrel_reason.formatField)(check.field);
|
|
143
143
|
return {
|
|
144
144
|
field: check.field,
|
|
@@ -150,27 +150,27 @@ function premiseCheck(check, actual, met, labels) {
|
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
152
|
/**
|
|
153
|
-
* Build
|
|
154
|
-
* check results.
|
|
153
|
+
* Build evidence rows from a quantitative factor's authored checks and
|
|
154
|
+
* evaluated check results.
|
|
155
155
|
*
|
|
156
156
|
* @param checks - The factor's authored checks
|
|
157
157
|
* @param results - The corresponding {@link CheckResult}s, in the same order
|
|
158
158
|
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
159
|
-
* @returns A fresh list of
|
|
159
|
+
* @returns A fresh list of evidence rows, one per authored check
|
|
160
160
|
*
|
|
161
161
|
* @example
|
|
162
162
|
* ```ts
|
|
163
163
|
* import { check } from '@orkestrel/reason'
|
|
164
|
-
* import {
|
|
164
|
+
* import { checkEvidence } from '@orkestrel/rater'
|
|
165
165
|
*
|
|
166
166
|
* const checks = [check('age', 'above', 18)]
|
|
167
|
-
*
|
|
167
|
+
* checkEvidence(checks, [{ field: 'age', met: true, actual: 25 }])
|
|
168
168
|
* ```
|
|
169
169
|
*/
|
|
170
|
-
function
|
|
170
|
+
function checkEvidence(checks, results, labels) {
|
|
171
171
|
return (checks ?? []).map((check, index) => {
|
|
172
172
|
const result = results?.[index];
|
|
173
|
-
return
|
|
173
|
+
return evidenceCheck(check, result?.actual, result?.met, labels);
|
|
174
174
|
});
|
|
175
175
|
}
|
|
176
176
|
/**
|
|
@@ -198,7 +198,7 @@ function worksheetFactor(definition, results, labels) {
|
|
|
198
198
|
...definition.description === void 0 ? {} : { description: definition.description },
|
|
199
199
|
applied: result?.applied ?? false,
|
|
200
200
|
...result?.value === void 0 ? {} : { value: result.value },
|
|
201
|
-
|
|
201
|
+
evidence: checkEvidence(definition.checks, result?.checks, labels)
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
204
|
/**
|
|
@@ -428,7 +428,7 @@ var Rater = class {
|
|
|
428
428
|
rate(input, subject) {
|
|
429
429
|
this.#ensureAlive();
|
|
430
430
|
const lines = this.#normalize(input);
|
|
431
|
-
if (!(0,
|
|
431
|
+
if (!(0, _orkestrel_reason.isSubject)(subject)) throw new RaterError("MISMATCH", "Subject must be a record");
|
|
432
432
|
const results = lines.map((line) => this.#rateLine(line, subject));
|
|
433
433
|
const total = (this.#total ?? sumAmounts)(results);
|
|
434
434
|
const success = results.every((entry) => entry.success);
|
|
@@ -458,7 +458,7 @@ var Rater = class {
|
|
|
458
458
|
const result = this.#engine.reason(subject, definition);
|
|
459
459
|
if (result.reasoning === "quantitative") return result;
|
|
460
460
|
const failure = (0, _orkestrel_reason.buildErrorResult)(definition, `Expected quantitative result, got ${result.reasoning}`);
|
|
461
|
-
const errors = [...result.errors, ...failure.errors];
|
|
461
|
+
const errors = [...(0, _orkestrel_contract.isArray)(result.errors) ? result.errors : [], ...failure.errors];
|
|
462
462
|
if (failure.reasoning === "quantitative") return {
|
|
463
463
|
...failure,
|
|
464
464
|
errors
|
|
@@ -551,14 +551,14 @@ function ratingDefinition(id, name, lines, overrides) {
|
|
|
551
551
|
//#endregion
|
|
552
552
|
exports.Rater = Rater;
|
|
553
553
|
exports.RaterError = RaterError;
|
|
554
|
-
exports.
|
|
554
|
+
exports.checkEvidence = checkEvidence;
|
|
555
555
|
exports.createRater = createRater;
|
|
556
|
+
exports.evidenceCheck = evidenceCheck;
|
|
556
557
|
exports.isLineDefinition = isLineDefinition;
|
|
557
558
|
exports.isRaterError = isRaterError;
|
|
558
559
|
exports.isRatingDefinition = isRatingDefinition;
|
|
559
560
|
exports.isStage = isStage;
|
|
560
561
|
exports.lineDefinition = lineDefinition;
|
|
561
|
-
exports.premiseCheck = premiseCheck;
|
|
562
562
|
exports.ratedLine = ratedLine;
|
|
563
563
|
exports.ratingDefinition = ratingDefinition;
|
|
564
564
|
exports.resultsWorksheet = resultsWorksheet;
|
|
@@ -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\tthis.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\tLineDefinition,\n\tLineResult,\n\tPremise,\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 Premise} 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 premise\n *\n * @example\n * ```ts\n * import { check } from '@orkestrel/reason'\n * import { premiseCheck } from '@orkestrel/rater'\n *\n * const entry = check('age', 'above', 18)\n * premiseCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }\n * ```\n */\nexport function premiseCheck(\n\tcheck: Check,\n\tactual: unknown,\n\tmet: boolean | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): Premise {\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 premises from a quantitative factor's authored checks and evaluated\n * 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 premises, one per authored check\n *\n * @example\n * ```ts\n * import { check } from '@orkestrel/reason'\n * import { checkPremises } from '@orkestrel/rater'\n *\n * const checks = [check('age', 'above', 18)]\n * checkPremises(checks, [{ field: 'age', met: true, actual: 25 }])\n * ```\n */\nexport function checkPremises(\n\tchecks: readonly Check[] | undefined,\n\tresults: readonly CheckResult[] | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): readonly Premise[] {\n\treturn (checks ?? []).map((check, index) => {\n\t\tconst result = results?.[index]\n\t\treturn premiseCheck(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\tpremises: checkPremises(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, isRecord } from '@orkestrel/contract'\nimport { buildErrorResult, createQuantitativeReasoner, createReason } 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>({ on: options?.on, error: options?.error })\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 (!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.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.\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 = [...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\tthis.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>({ on: options?.on, error: options?.error })\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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"}
|
|
@@ -17,24 +17,24 @@ import { ReasonInterface } from '@orkestrel/reason';
|
|
|
17
17
|
import { Subject } from '@orkestrel/reason';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Build
|
|
21
|
-
* check results.
|
|
20
|
+
* Build evidence rows from a quantitative factor's authored checks and
|
|
21
|
+
* evaluated check results.
|
|
22
22
|
*
|
|
23
23
|
* @param checks - The factor's authored checks
|
|
24
24
|
* @param results - The corresponding {@link CheckResult}s, in the same order
|
|
25
25
|
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
26
|
-
* @returns A fresh list of
|
|
26
|
+
* @returns A fresh list of evidence rows, one per authored check
|
|
27
27
|
*
|
|
28
28
|
* @example
|
|
29
29
|
* ```ts
|
|
30
30
|
* import { check } from '@orkestrel/reason'
|
|
31
|
-
* import {
|
|
31
|
+
* import { checkEvidence } from '@orkestrel/rater'
|
|
32
32
|
*
|
|
33
33
|
* const checks = [check('age', 'above', 18)]
|
|
34
|
-
*
|
|
34
|
+
* checkEvidence(checks, [{ field: 'age', met: true, actual: 25 }])
|
|
35
35
|
* ```
|
|
36
36
|
*/
|
|
37
|
-
export declare function
|
|
37
|
+
export declare function checkEvidence(checks: readonly Check[] | undefined, results: readonly CheckResult[] | undefined, labels?: Readonly<Record<string, string>>): readonly Evidence[];
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Create a rating orchestrator.
|
|
@@ -52,6 +52,36 @@ export declare function checkPremises(checks: readonly Check[] | undefined, resu
|
|
|
52
52
|
*/
|
|
53
53
|
export declare function createRater(options?: RaterOptions): RaterInterface;
|
|
54
54
|
|
|
55
|
+
/** A checked-evidence row rendered into a display-neutral sentence. */
|
|
56
|
+
export declare interface Evidence {
|
|
57
|
+
readonly field?: FieldPath;
|
|
58
|
+
readonly label?: string;
|
|
59
|
+
readonly comparison?: Comparison;
|
|
60
|
+
readonly expected?: unknown;
|
|
61
|
+
readonly actual?: unknown;
|
|
62
|
+
readonly met?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build an {@link Evidence} row from an evaluated {@link Check}.
|
|
67
|
+
*
|
|
68
|
+
* @param check - The authored check
|
|
69
|
+
* @param actual - The resolved subject value
|
|
70
|
+
* @param met - Whether the check was met (absent when not yet evaluated)
|
|
71
|
+
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
72
|
+
* @returns A fresh evidence row
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { check } from '@orkestrel/reason'
|
|
77
|
+
* import { evidenceCheck } from '@orkestrel/rater'
|
|
78
|
+
*
|
|
79
|
+
* const entry = check('age', 'above', 18)
|
|
80
|
+
* evidenceCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function evidenceCheck(check: Check, actual: unknown, met: boolean | undefined, labels?: Readonly<Record<string, string>>): Evidence;
|
|
84
|
+
|
|
55
85
|
/**
|
|
56
86
|
* Determine whether a value is an exact {@link LineDefinition} record.
|
|
57
87
|
*
|
|
@@ -176,36 +206,6 @@ export declare interface LineResult {
|
|
|
176
206
|
readonly success: boolean;
|
|
177
207
|
}
|
|
178
208
|
|
|
179
|
-
/** A shared checked-evidence row rendered into a display-neutral sentence. */
|
|
180
|
-
export declare interface Premise {
|
|
181
|
-
readonly field?: FieldPath;
|
|
182
|
-
readonly label?: string;
|
|
183
|
-
readonly comparison?: Comparison;
|
|
184
|
-
readonly expected?: unknown;
|
|
185
|
-
readonly actual?: unknown;
|
|
186
|
-
readonly met?: boolean;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Build a {@link Premise} from an evaluated {@link Check}.
|
|
191
|
-
*
|
|
192
|
-
* @param check - The authored check
|
|
193
|
-
* @param actual - The resolved subject value
|
|
194
|
-
* @param met - Whether the check was met (absent when not yet evaluated)
|
|
195
|
-
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
196
|
-
* @returns A fresh premise
|
|
197
|
-
*
|
|
198
|
-
* @example
|
|
199
|
-
* ```ts
|
|
200
|
-
* import { check } from '@orkestrel/reason'
|
|
201
|
-
* import { premiseCheck } from '@orkestrel/rater'
|
|
202
|
-
*
|
|
203
|
-
* const entry = check('age', 'above', 18)
|
|
204
|
-
* premiseCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
|
|
205
|
-
* ```
|
|
206
|
-
*/
|
|
207
|
-
export declare function premiseCheck(check: Check, actual: unknown, met: boolean | undefined, labels?: Readonly<Record<string, string>>): Premise;
|
|
208
|
-
|
|
209
209
|
/**
|
|
210
210
|
* Build a rated {@link LineResult} from a line's evaluated
|
|
211
211
|
* {@link QuantitativeResult}.
|
|
@@ -315,7 +315,7 @@ export declare interface RaterInterface {
|
|
|
315
315
|
* and OWNS its own quantitative-only engine (`bail: false`), destroying it on
|
|
316
316
|
* `destroy()`. `total` — a {@link TotalHandler} overriding the default
|
|
317
317
|
* {@link sumAmounts} projection. `labels` — field-to-label display overrides
|
|
318
|
-
* threaded into every resolved {@link
|
|
318
|
+
* threaded into every resolved {@link Evidence}.
|
|
319
319
|
*/
|
|
320
320
|
export declare interface RaterOptions {
|
|
321
321
|
readonly on?: EmitterHooks<RaterEventMap>;
|
|
@@ -452,7 +452,7 @@ export declare interface WorksheetFactor {
|
|
|
452
452
|
readonly description?: string;
|
|
453
453
|
readonly applied: boolean;
|
|
454
454
|
readonly value?: number;
|
|
455
|
-
readonly
|
|
455
|
+
readonly evidence: readonly Evidence[];
|
|
456
456
|
}
|
|
457
457
|
|
|
458
458
|
/**
|
package/dist/src/core/index.d.ts
CHANGED
|
@@ -17,24 +17,24 @@ import { ReasonInterface } from '@orkestrel/reason';
|
|
|
17
17
|
import { Subject } from '@orkestrel/reason';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Build
|
|
21
|
-
* check results.
|
|
20
|
+
* Build evidence rows from a quantitative factor's authored checks and
|
|
21
|
+
* evaluated check results.
|
|
22
22
|
*
|
|
23
23
|
* @param checks - The factor's authored checks
|
|
24
24
|
* @param results - The corresponding {@link CheckResult}s, in the same order
|
|
25
25
|
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
26
|
-
* @returns A fresh list of
|
|
26
|
+
* @returns A fresh list of evidence rows, one per authored check
|
|
27
27
|
*
|
|
28
28
|
* @example
|
|
29
29
|
* ```ts
|
|
30
30
|
* import { check } from '@orkestrel/reason'
|
|
31
|
-
* import {
|
|
31
|
+
* import { checkEvidence } from '@orkestrel/rater'
|
|
32
32
|
*
|
|
33
33
|
* const checks = [check('age', 'above', 18)]
|
|
34
|
-
*
|
|
34
|
+
* checkEvidence(checks, [{ field: 'age', met: true, actual: 25 }])
|
|
35
35
|
* ```
|
|
36
36
|
*/
|
|
37
|
-
export declare function
|
|
37
|
+
export declare function checkEvidence(checks: readonly Check[] | undefined, results: readonly CheckResult[] | undefined, labels?: Readonly<Record<string, string>>): readonly Evidence[];
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Create a rating orchestrator.
|
|
@@ -52,6 +52,36 @@ export declare function checkPremises(checks: readonly Check[] | undefined, resu
|
|
|
52
52
|
*/
|
|
53
53
|
export declare function createRater(options?: RaterOptions): RaterInterface;
|
|
54
54
|
|
|
55
|
+
/** A checked-evidence row rendered into a display-neutral sentence. */
|
|
56
|
+
export declare interface Evidence {
|
|
57
|
+
readonly field?: FieldPath;
|
|
58
|
+
readonly label?: string;
|
|
59
|
+
readonly comparison?: Comparison;
|
|
60
|
+
readonly expected?: unknown;
|
|
61
|
+
readonly actual?: unknown;
|
|
62
|
+
readonly met?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build an {@link Evidence} row from an evaluated {@link Check}.
|
|
67
|
+
*
|
|
68
|
+
* @param check - The authored check
|
|
69
|
+
* @param actual - The resolved subject value
|
|
70
|
+
* @param met - Whether the check was met (absent when not yet evaluated)
|
|
71
|
+
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
72
|
+
* @returns A fresh evidence row
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { check } from '@orkestrel/reason'
|
|
77
|
+
* import { evidenceCheck } from '@orkestrel/rater'
|
|
78
|
+
*
|
|
79
|
+
* const entry = check('age', 'above', 18)
|
|
80
|
+
* evidenceCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function evidenceCheck(check: Check, actual: unknown, met: boolean | undefined, labels?: Readonly<Record<string, string>>): Evidence;
|
|
84
|
+
|
|
55
85
|
/**
|
|
56
86
|
* Determine whether a value is an exact {@link LineDefinition} record.
|
|
57
87
|
*
|
|
@@ -176,36 +206,6 @@ export declare interface LineResult {
|
|
|
176
206
|
readonly success: boolean;
|
|
177
207
|
}
|
|
178
208
|
|
|
179
|
-
/** A shared checked-evidence row rendered into a display-neutral sentence. */
|
|
180
|
-
export declare interface Premise {
|
|
181
|
-
readonly field?: FieldPath;
|
|
182
|
-
readonly label?: string;
|
|
183
|
-
readonly comparison?: Comparison;
|
|
184
|
-
readonly expected?: unknown;
|
|
185
|
-
readonly actual?: unknown;
|
|
186
|
-
readonly met?: boolean;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Build a {@link Premise} from an evaluated {@link Check}.
|
|
191
|
-
*
|
|
192
|
-
* @param check - The authored check
|
|
193
|
-
* @param actual - The resolved subject value
|
|
194
|
-
* @param met - Whether the check was met (absent when not yet evaluated)
|
|
195
|
-
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
196
|
-
* @returns A fresh premise
|
|
197
|
-
*
|
|
198
|
-
* @example
|
|
199
|
-
* ```ts
|
|
200
|
-
* import { check } from '@orkestrel/reason'
|
|
201
|
-
* import { premiseCheck } from '@orkestrel/rater'
|
|
202
|
-
*
|
|
203
|
-
* const entry = check('age', 'above', 18)
|
|
204
|
-
* premiseCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
|
|
205
|
-
* ```
|
|
206
|
-
*/
|
|
207
|
-
export declare function premiseCheck(check: Check, actual: unknown, met: boolean | undefined, labels?: Readonly<Record<string, string>>): Premise;
|
|
208
|
-
|
|
209
209
|
/**
|
|
210
210
|
* Build a rated {@link LineResult} from a line's evaluated
|
|
211
211
|
* {@link QuantitativeResult}.
|
|
@@ -315,7 +315,7 @@ export declare interface RaterInterface {
|
|
|
315
315
|
* and OWNS its own quantitative-only engine (`bail: false`), destroying it on
|
|
316
316
|
* `destroy()`. `total` — a {@link TotalHandler} overriding the default
|
|
317
317
|
* {@link sumAmounts} projection. `labels` — field-to-label display overrides
|
|
318
|
-
* threaded into every resolved {@link
|
|
318
|
+
* threaded into every resolved {@link Evidence}.
|
|
319
319
|
*/
|
|
320
320
|
export declare interface RaterOptions {
|
|
321
321
|
readonly on?: EmitterHooks<RaterEventMap>;
|
|
@@ -452,7 +452,7 @@ export declare interface WorksheetFactor {
|
|
|
452
452
|
readonly description?: string;
|
|
453
453
|
readonly applied: boolean;
|
|
454
454
|
readonly value?: number;
|
|
455
|
-
readonly
|
|
455
|
+
readonly evidence: readonly Evidence[];
|
|
456
456
|
}
|
|
457
457
|
|
|
458
458
|
/**
|
package/dist/src/core/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { arrayOf,
|
|
2
|
-
import { buildErrorResult, createQuantitativeReasoner, createReason, formatField, isQuantitativeDefinition } from "@orkestrel/reason";
|
|
1
|
+
import { arrayOf, isArray, isJSONValue, isString, literalOf, recordOf } from "@orkestrel/contract";
|
|
2
|
+
import { buildErrorResult, createQuantitativeReasoner, createReason, formatField, isQuantitativeDefinition, isSubject } from "@orkestrel/reason";
|
|
3
3
|
import { Emitter } from "@orkestrel/emitter";
|
|
4
4
|
//#region src/core/errors.ts
|
|
5
5
|
/**
|
|
@@ -120,24 +120,24 @@ function isRatingDefinition(value) {
|
|
|
120
120
|
//#endregion
|
|
121
121
|
//#region src/core/helpers.ts
|
|
122
122
|
/**
|
|
123
|
-
* Build
|
|
123
|
+
* Build an {@link Evidence} row from an evaluated {@link Check}.
|
|
124
124
|
*
|
|
125
125
|
* @param check - The authored check
|
|
126
126
|
* @param actual - The resolved subject value
|
|
127
127
|
* @param met - Whether the check was met (absent when not yet evaluated)
|
|
128
128
|
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
129
|
-
* @returns A fresh
|
|
129
|
+
* @returns A fresh evidence row
|
|
130
130
|
*
|
|
131
131
|
* @example
|
|
132
132
|
* ```ts
|
|
133
133
|
* import { check } from '@orkestrel/reason'
|
|
134
|
-
* import {
|
|
134
|
+
* import { evidenceCheck } from '@orkestrel/rater'
|
|
135
135
|
*
|
|
136
136
|
* const entry = check('age', 'above', 18)
|
|
137
|
-
*
|
|
137
|
+
* evidenceCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
|
|
138
138
|
* ```
|
|
139
139
|
*/
|
|
140
|
-
function
|
|
140
|
+
function evidenceCheck(check, actual, met, labels) {
|
|
141
141
|
const field = formatField(check.field);
|
|
142
142
|
return {
|
|
143
143
|
field: check.field,
|
|
@@ -149,27 +149,27 @@ function premiseCheck(check, actual, met, labels) {
|
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
151
|
/**
|
|
152
|
-
* Build
|
|
153
|
-
* check results.
|
|
152
|
+
* Build evidence rows from a quantitative factor's authored checks and
|
|
153
|
+
* evaluated check results.
|
|
154
154
|
*
|
|
155
155
|
* @param checks - The factor's authored checks
|
|
156
156
|
* @param results - The corresponding {@link CheckResult}s, in the same order
|
|
157
157
|
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
158
|
-
* @returns A fresh list of
|
|
158
|
+
* @returns A fresh list of evidence rows, one per authored check
|
|
159
159
|
*
|
|
160
160
|
* @example
|
|
161
161
|
* ```ts
|
|
162
162
|
* import { check } from '@orkestrel/reason'
|
|
163
|
-
* import {
|
|
163
|
+
* import { checkEvidence } from '@orkestrel/rater'
|
|
164
164
|
*
|
|
165
165
|
* const checks = [check('age', 'above', 18)]
|
|
166
|
-
*
|
|
166
|
+
* checkEvidence(checks, [{ field: 'age', met: true, actual: 25 }])
|
|
167
167
|
* ```
|
|
168
168
|
*/
|
|
169
|
-
function
|
|
169
|
+
function checkEvidence(checks, results, labels) {
|
|
170
170
|
return (checks ?? []).map((check, index) => {
|
|
171
171
|
const result = results?.[index];
|
|
172
|
-
return
|
|
172
|
+
return evidenceCheck(check, result?.actual, result?.met, labels);
|
|
173
173
|
});
|
|
174
174
|
}
|
|
175
175
|
/**
|
|
@@ -197,7 +197,7 @@ function worksheetFactor(definition, results, labels) {
|
|
|
197
197
|
...definition.description === void 0 ? {} : { description: definition.description },
|
|
198
198
|
applied: result?.applied ?? false,
|
|
199
199
|
...result?.value === void 0 ? {} : { value: result.value },
|
|
200
|
-
|
|
200
|
+
evidence: checkEvidence(definition.checks, result?.checks, labels)
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
203
|
/**
|
|
@@ -427,7 +427,7 @@ var Rater = class {
|
|
|
427
427
|
rate(input, subject) {
|
|
428
428
|
this.#ensureAlive();
|
|
429
429
|
const lines = this.#normalize(input);
|
|
430
|
-
if (!
|
|
430
|
+
if (!isSubject(subject)) throw new RaterError("MISMATCH", "Subject must be a record");
|
|
431
431
|
const results = lines.map((line) => this.#rateLine(line, subject));
|
|
432
432
|
const total = (this.#total ?? sumAmounts)(results);
|
|
433
433
|
const success = results.every((entry) => entry.success);
|
|
@@ -457,7 +457,7 @@ var Rater = class {
|
|
|
457
457
|
const result = this.#engine.reason(subject, definition);
|
|
458
458
|
if (result.reasoning === "quantitative") return result;
|
|
459
459
|
const failure = buildErrorResult(definition, `Expected quantitative result, got ${result.reasoning}`);
|
|
460
|
-
const errors = [...result.errors, ...failure.errors];
|
|
460
|
+
const errors = [...isArray(result.errors) ? result.errors : [], ...failure.errors];
|
|
461
461
|
if (failure.reasoning === "quantitative") return {
|
|
462
462
|
...failure,
|
|
463
463
|
errors
|
|
@@ -548,6 +548,6 @@ function ratingDefinition(id, name, lines, overrides) {
|
|
|
548
548
|
};
|
|
549
549
|
}
|
|
550
550
|
//#endregion
|
|
551
|
-
export { Rater, RaterError,
|
|
551
|
+
export { Rater, RaterError, checkEvidence, createRater, evidenceCheck, isLineDefinition, isRaterError, isRatingDefinition, isStage, lineDefinition, ratedLine, ratingDefinition, resultsWorksheet, sumAmounts, worksheetFactor, worksheetGroup, worksheetStep, worksheetSteps };
|
|
552
552
|
|
|
553
553
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","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\tthis.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\tLineDefinition,\n\tLineResult,\n\tPremise,\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 Premise} 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 premise\n *\n * @example\n * ```ts\n * import { check } from '@orkestrel/reason'\n * import { premiseCheck } from '@orkestrel/rater'\n *\n * const entry = check('age', 'above', 18)\n * premiseCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }\n * ```\n */\nexport function premiseCheck(\n\tcheck: Check,\n\tactual: unknown,\n\tmet: boolean | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): Premise {\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 premises from a quantitative factor's authored checks and evaluated\n * 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 premises, one per authored check\n *\n * @example\n * ```ts\n * import { check } from '@orkestrel/reason'\n * import { checkPremises } from '@orkestrel/rater'\n *\n * const checks = [check('age', 'above', 18)]\n * checkPremises(checks, [{ field: 'age', met: true, actual: 25 }])\n * ```\n */\nexport function checkPremises(\n\tchecks: readonly Check[] | undefined,\n\tresults: readonly CheckResult[] | undefined,\n\tlabels?: Readonly<Record<string, string>>,\n): readonly Premise[] {\n\treturn (checks ?? []).map((check, index) => {\n\t\tconst result = results?.[index]\n\t\treturn premiseCheck(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\tpremises: checkPremises(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, isRecord } from '@orkestrel/contract'\nimport { buildErrorResult, createQuantitativeReasoner, createReason } 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>({ on: options?.on, error: options?.error })\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 (!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.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.\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 = [...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.js","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\tthis.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>({ on: options?.on, error: options?.error })\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":";;;;;;;;;;;;;;;;;;;AAiBA,IAAa,aAAb,cAAgC,MAAM;CACrC;CACA;CAEA,YAAY,MAAsB,SAAiB,SAA6C;EAC/F,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,UAAU;CAChB;AACD;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,aAAa,OAAqC;CACjE,OAAO,iBAAiB;AACzB;;;;;;;;;;;;;;;;;AC7BA,IAAa,UAAwB,UAAU,UAAU,SAAS,OAAO;;;;;;;;;;;;;;;;;;;AAoBzE,SAAgB,iBAAiB,OAAyC;CACzE,OAAO,SACN;EACC,IAAI;EACJ,MAAM;EACN,aAAa;EACb,MAAM;EACN,UAAU;CACX,GACA,CAAC,eAAe,UAAU,CAC3B,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,mBAAmB,OAA2C;CAC7E,OAAO,SACN;EACC,IAAI;EACJ,MAAM;EACN,aAAa;EACb,OAAO,QAAQ,gBAAgB;EAC/B,UAAU;CACX,GACA,CAAC,eAAe,UAAU,CAC3B,CAAC,CAAC,KAAK;AACR;;;;;;;;;;;;;;;;;;;;;ACzCA,SAAgB,cACf,OACA,QACA,KACA,QACW;CACX,MAAM,QAAQ,YAAY,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,cACf,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,gBACf,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,cAAc,WAAW,QAAQ,QAAQ,QAAQ,MAAM;CAClE;AACD;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,eACf,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,gBAAgB,QAAQ,QAAQ,WAAW,CAAC,GAAG,MAAM,CACtD;CACD;AACD;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,cACf,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,eACf,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,cACC,UACA,OAAO,IACP,OAAO,MACP,OAAO,OACP,GAAG,OAAO,GAAG,KAAK,OAAO,OAC1B,CACD;EAGF,OAAO,KACN,cAAc,SAAS,MAAM,IAAI,MAAM,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,KAAK,MAAM,OAAO,CACzF;CACD;CACA,OAAO,KACN,cACC,SACA,WAAW,IACX,WAAW,MACX,OAAO,OACP,GAAG,WAAW,YAAY,KAAK,OAAO,OACvC,CACD;CACA,OAAO;AACR;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,iBACf,YACA,QACA,QACY;CACZ,MAAM,SAAS,WAAW,OAAO,KAAK,UAAU,eAAe,OAAO,OAAO,QAAQ,MAAM,CAAC;CAC5F,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,eAAe,YAAY,QAAQ,MAAM;EAChD,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,SAAS,OAAO;CACjB;AACD;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,UACf,YACA,QACA,QACa;CACb,OAAO;EACN,IAAI,WAAW;EACf,MAAM,WAAW;EACjB,GAAI,OAAO,UAAU,EAAE,QAAQ,OAAO,MAAM,IAAI,CAAC;EACjD,WAAW,iBAAiB,WAAW,MAAM,QAAQ,MAAM;EAC3D,SAAS,OAAO;CACjB;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;;;;;;;;;;;;;;;;;;;;;;;;;ACnSA,IAAa,QAAb,MAA6C;CAC5C;CACA;CACA;CACA;CACA;CACA,aAAa;CAEb,YAAY,SAAwB;EACnC,KAAKA,WAAW,IAAI,QAAuB;GAAE,IAAI,SAAS;GAAI,OAAO,SAAS;EAAM,CAAC;EACrF,IAAI,SAAS,WAAW,KAAA,GAAW;GAClC,KAAKC,UAAU,aAAa;IAAE,WAAW,CAAC,2BAA2B,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,CAAC,UAAU,OAAO,GAAG,MAAM,IAAI,WAAW,YAAY,0BAA0B;EACpF,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,OAAO;EACtD,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,IAAI,QAAQ,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,UAAU,MADF,KAAKS,oBAAoB,SAAS,KAAK,IAC/B,GAAQ,KAAKL,OAAO;CAC5C;CAQA,oBAAoB,SAAkB,YAAwD;EAC7F,MAAM,SAAS,KAAKH,QAAQ,OAAO,SAAS,UAAU;EACtD,IAAI,OAAO,cAAc,gBAAgB,OAAO;EAChD,MAAM,UAAU,iBACf,YACA,qCAAqC,OAAO,WAC7C;EACA,MAAM,SAAS,CAAC,GAAI,QAAgB,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;;;;;;;;;;;;;;;;;AC/HA,SAAgB,YAAY,SAAwC;CACnE,OAAO,IAAI,MAAM,OAAO;AACzB;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,eACf,IACA,MACA,MACA,WACiB;CACjB,OAAO;EAAE;EAAI;EAAM;EAAM,GAAG;CAAU;AACvC;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,iBACf,IACA,MACA,OACA,WACmB;CACnB,OAAO;EAAE;EAAI;EAAM;EAAO,GAAG;CAAU;AACxC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orkestrel/rater",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "A typed quantitative rating layer: authored lines are rated against a subject to produce per-line amounts, worksheets, and a derived total. Part of the @orkestrel line.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"quantitative",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"copy": "node -e \"const fs=require('node:fs'),p=require('node:path'),a=process.argv[1],b=process.argv[2];fs.mkdirSync(p.dirname(b),{recursive:true});fs.cpSync(a,b,{force:true});console.log('Copied: '+a+' to '+b)\"",
|
|
50
50
|
"tmp:txt": "node -e \"const fs=require('node:fs'),p=require('node:path');function walk(d){for(const e of fs.readdirSync(d,{withFileTypes:true})){const f=p.join(d,e.name);if(e.isDirectory()){walk(f)}else if(!e.name.endsWith('.md')&&!e.name.endsWith('.txt')){const t=f+'.txt';if(!fs.existsSync(t)){fs.renameSync(f,t)}else{console.warn('Skipping '+f+' — target exists: '+t)}}}}try{walk('tmp')}catch(e){if(e.code!=='ENOENT')throw e}\"",
|
|
51
51
|
"lint": "oxlint --config .oxlintrc.json --fix .",
|
|
52
|
-
"check": "tsc --noEmit --project tsconfig.json",
|
|
52
|
+
"check": "tsc --noEmit --project tsconfig.json && npm run check:src",
|
|
53
53
|
"check:src": "npm run check:src:core",
|
|
54
54
|
"check:src:core": "tsc --noEmit -p configs/src/tsconfig.core.json",
|
|
55
55
|
"format": "oxfmt --config .oxfmtrc.json --write .",
|
|
@@ -62,25 +62,25 @@
|
|
|
62
62
|
"build": "npm run clean && npm run build:src",
|
|
63
63
|
"build:src": "npm run build:src:core",
|
|
64
64
|
"build:src:core": "vite build --config configs/src/vite.core.config.ts && npm run copy dist/src/core/index.d.ts dist/src/core/index.d.cts",
|
|
65
|
-
"prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run
|
|
65
|
+
"prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@orkestrel/contract": "^0.0.
|
|
69
|
-
"@orkestrel/emitter": "^0.0.
|
|
70
|
-
"@orkestrel/reason": "^0.0.
|
|
68
|
+
"@orkestrel/contract": "^0.0.2",
|
|
69
|
+
"@orkestrel/emitter": "^0.0.2",
|
|
70
|
+
"@orkestrel/reason": "^0.0.2"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
|
-
"@microsoft/api-extractor": "^7.58.
|
|
74
|
-
"@orkestrel/guide": "^0.0.
|
|
73
|
+
"@microsoft/api-extractor": "^7.58.11",
|
|
74
|
+
"@orkestrel/guide": "^0.0.2",
|
|
75
75
|
"@types/node": "^26.1.1",
|
|
76
|
-
"oxfmt": "^0.
|
|
77
|
-
"oxlint": "^1.
|
|
76
|
+
"oxfmt": "^0.59.0",
|
|
77
|
+
"oxlint": "^1.74.0",
|
|
78
78
|
"typescript": "^6.0.3",
|
|
79
|
-
"vite": "^8.1.
|
|
79
|
+
"vite": "^8.1.5",
|
|
80
80
|
"vite-plugin-dts": "^5.0.3",
|
|
81
81
|
"vitest": "^4.1.10"
|
|
82
82
|
},
|
|
83
83
|
"engines": {
|
|
84
|
-
"node": ">=
|
|
84
|
+
"node": ">=22"
|
|
85
85
|
}
|
|
86
86
|
}
|