@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.
@@ -121,24 +121,24 @@ function isRatingDefinition(value) {
121
121
  //#endregion
122
122
  //#region src/core/helpers.ts
123
123
  /**
124
- * Build a {@link Premise} from an evaluated {@link Check}.
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 premise
130
+ * @returns A fresh evidence row
131
131
  *
132
132
  * @example
133
133
  * ```ts
134
134
  * import { check } from '@orkestrel/reason'
135
- * import { premiseCheck } from '@orkestrel/rater'
135
+ * import { evidenceCheck } from '@orkestrel/rater'
136
136
  *
137
137
  * const entry = check('age', 'above', 18)
138
- * premiseCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
138
+ * evidenceCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
139
139
  * ```
140
140
  */
141
- function premiseCheck(check, actual, met, labels) {
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 premises from a quantitative factor's authored checks and evaluated
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 premises, one per authored check
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 { checkPremises } from '@orkestrel/rater'
164
+ * import { checkEvidence } from '@orkestrel/rater'
165
165
  *
166
166
  * const checks = [check('age', 'above', 18)]
167
- * checkPremises(checks, [{ field: 'age', met: true, actual: 25 }])
167
+ * checkEvidence(checks, [{ field: 'age', met: true, actual: 25 }])
168
168
  * ```
169
169
  */
170
- function checkPremises(checks, results, labels) {
170
+ function checkEvidence(checks, results, labels) {
171
171
  return (checks ?? []).map((check, index) => {
172
172
  const result = results?.[index];
173
- return premiseCheck(check, result?.actual, result?.met, labels);
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
- premises: checkPremises(definition.checks, result?.checks, labels)
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, _orkestrel_contract.isRecord)(subject)) throw new RaterError("MISMATCH", "Subject must be a record");
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.checkPremises = checkPremises;
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 premises from a quantitative factor's authored checks and evaluated
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 premises, one per authored check
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 { checkPremises } from '@orkestrel/rater'
31
+ * import { checkEvidence } from '@orkestrel/rater'
32
32
  *
33
33
  * const checks = [check('age', 'above', 18)]
34
- * checkPremises(checks, [{ field: 'age', met: true, actual: 25 }])
34
+ * checkEvidence(checks, [{ field: 'age', met: true, actual: 25 }])
35
35
  * ```
36
36
  */
37
- export declare function checkPremises(checks: readonly Check[] | undefined, results: readonly CheckResult[] | undefined, labels?: Readonly<Record<string, string>>): readonly Premise[];
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 Premise}.
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 premises: readonly Premise[];
455
+ readonly evidence: readonly Evidence[];
456
456
  }
457
457
 
458
458
  /**
@@ -17,24 +17,24 @@ import { ReasonInterface } from '@orkestrel/reason';
17
17
  import { Subject } from '@orkestrel/reason';
18
18
 
19
19
  /**
20
- * Build premises from a quantitative factor's authored checks and evaluated
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 premises, one per authored check
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 { checkPremises } from '@orkestrel/rater'
31
+ * import { checkEvidence } from '@orkestrel/rater'
32
32
  *
33
33
  * const checks = [check('age', 'above', 18)]
34
- * checkPremises(checks, [{ field: 'age', met: true, actual: 25 }])
34
+ * checkEvidence(checks, [{ field: 'age', met: true, actual: 25 }])
35
35
  * ```
36
36
  */
37
- export declare function checkPremises(checks: readonly Check[] | undefined, results: readonly CheckResult[] | undefined, labels?: Readonly<Record<string, string>>): readonly Premise[];
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 Premise}.
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 premises: readonly Premise[];
455
+ readonly evidence: readonly Evidence[];
456
456
  }
457
457
 
458
458
  /**
@@ -1,5 +1,5 @@
1
- import { arrayOf, isJSONValue, isRecord, isString, literalOf, recordOf } from "@orkestrel/contract";
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 a {@link Premise} from an evaluated {@link Check}.
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 premise
129
+ * @returns A fresh evidence row
130
130
  *
131
131
  * @example
132
132
  * ```ts
133
133
  * import { check } from '@orkestrel/reason'
134
- * import { premiseCheck } from '@orkestrel/rater'
134
+ * import { evidenceCheck } from '@orkestrel/rater'
135
135
  *
136
136
  * const entry = check('age', 'above', 18)
137
- * premiseCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
137
+ * evidenceCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
138
138
  * ```
139
139
  */
140
- function premiseCheck(check, actual, met, labels) {
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 premises from a quantitative factor's authored checks and evaluated
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 premises, one per authored check
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 { checkPremises } from '@orkestrel/rater'
163
+ * import { checkEvidence } from '@orkestrel/rater'
164
164
  *
165
165
  * const checks = [check('age', 'above', 18)]
166
- * checkPremises(checks, [{ field: 'age', met: true, actual: 25 }])
166
+ * checkEvidence(checks, [{ field: 'age', met: true, actual: 25 }])
167
167
  * ```
168
168
  */
169
- function checkPremises(checks, results, labels) {
169
+ function checkEvidence(checks, results, labels) {
170
170
  return (checks ?? []).map((check, index) => {
171
171
  const result = results?.[index];
172
- return premiseCheck(check, result?.actual, result?.met, labels);
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
- premises: checkPremises(definition.checks, result?.checks, labels)
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 (!isRecord(subject)) throw new RaterError("MISMATCH", "Subject must be a record");
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, checkPremises, createRater, isLineDefinition, isRaterError, isRatingDefinition, isStage, lineDefinition, premiseCheck, ratedLine, ratingDefinition, resultsWorksheet, sumAmounts, worksheetFactor, worksheetGroup, worksheetStep, worksheetSteps };
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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orkestrel/rater",
3
- "version": "0.0.2",
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 check:src && npm run build && npm test"
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.1",
69
- "@orkestrel/emitter": "^0.0.1",
70
- "@orkestrel/reason": "^0.0.1"
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.9",
74
- "@orkestrel/guide": "^0.0.1",
73
+ "@microsoft/api-extractor": "^7.58.11",
74
+ "@orkestrel/guide": "^0.0.2",
75
75
  "@types/node": "^26.1.1",
76
- "oxfmt": "^0.58.0",
77
- "oxlint": "^1.73.0",
76
+ "oxfmt": "^0.59.0",
77
+ "oxlint": "^1.74.0",
78
78
  "typescript": "^6.0.3",
79
- "vite": "^8.1.4",
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": ">=24"
84
+ "node": ">=22"
85
85
  }
86
86
  }