@orkestrel/qualifier 0.0.11 → 0.0.13

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.
@@ -1,18 +1,18 @@
1
1
  import { arrayOf, isBoolean, isFiniteNumber, isNumber, isRecord, isString, literalOf, objectOf, recordOf, resolveField, whereOf } from "@orkestrel/contract";
2
- import { createEvaluator, createLogicalReasoner, createQuantitativeReasoner, createReason, extractAtoms, extractConclusions, findDuplicates, formatField, isBounds, isComparison, isFieldPath, isLogicalDefinition, isQuantitativeDefinition, isReasonError, isReasonValidationResult } from "@orkestrel/reason";
2
+ import { createEvaluator, createLogicalReasoner, createQuantitativeReasoner, createReason, extractAtoms, extractConclusions, findDuplicates, formatField, isBounds, isComparison, isFieldPath, isLogicalDefinition, isQuantitativeDefinition, isReasonError } from "@orkestrel/reason";
3
3
  import { Emitter } from "@orkestrel/emitter";
4
4
  //#region src/core/constants.ts
5
- /** Default definition validation policy for `createQualifier` / `Qualifier.qualify`. */
5
+ /** Holds the default definition validation policy for `createQualifier` / `Qualifier.qualify`. */
6
6
  var DEFAULT_QUALIFIER_VALIDATE = true;
7
- /** The reserved internal projection namespace a pass's working projection is written under. */
7
+ /** Names the reserved internal projection namespace a pass's working projection is written under. */
8
8
  var QUALIFICATION_KEY = "qualification";
9
- /** Eligibility severity order — most to least severe. */
9
+ /** Lists the eligibility severities in order — most to least severe. */
10
10
  var ELIGIBILITY_PRECEDENCE = Object.freeze([
11
11
  "ineligible",
12
12
  "referral",
13
13
  "eligible"
14
14
  ]);
15
- /** The eligibility impact of each {@link QualificationEffect}; `condition` remains eligible. */
15
+ /** Maps each {@link QualificationEffect} to its eligibility impact; `condition` remains eligible. */
16
16
  var EFFECT_ELIGIBILITIES = Object.freeze({
17
17
  restriction: "ineligible",
18
18
  referral: "referral",
@@ -21,14 +21,19 @@ var EFFECT_ELIGIBILITIES = Object.freeze({
21
21
  //#endregion
22
22
  //#region src/core/errors.ts
23
23
  /**
24
- * A coded programmer error thrown by the qualifier layer.
24
+ * Represents a coded programmer error thrown by the qualifier layer.
25
25
  *
26
26
  * @remarks
27
- * `DEFINITION` — a qualification definition failed semantic validation.
28
- * `MISMATCH` — a subject is not a record or already carries the reserved
29
- * `qualification` key. `DESTROYED` — use of a destroyed qualifier.
30
- * `ENGINE` — the underlying reason engine threw while running a pass (the
31
- * original throw is preserved as `context.cause`).
27
+ * `DEFINITION` — a qualification definition failed semantic validation; when
28
+ * `qualify`'s own validation raises it, `context.definition` names the
29
+ * definition, and when an engine throw maps to it, `context.pass` names the pass
30
+ * and `context.cause` preserves the original throw. `MISMATCH` — a subject is
31
+ * not a record or already carries the reserved `qualification` key.
32
+ * `DESTROYED` — use of a destroyed qualifier; an engine-mapped one carries
33
+ * `context.pass` and `context.cause`. `ENGINE` — the underlying reason engine
34
+ * threw while running a pass; `context.pass` names the pass and `context.cause`
35
+ * preserves the original throw. `context` is absent for a throw that carries no
36
+ * payload.
32
37
  */
33
38
  var QualifierError = class extends Error {
34
39
  code;
@@ -40,31 +45,47 @@ var QualifierError = class extends Error {
40
45
  this.context = context;
41
46
  }
42
47
  };
43
- /** Narrow a caught value to a {@link QualifierError}. */
48
+ /**
49
+ * Narrows a caught value to a {@link QualifierError}.
50
+ *
51
+ * @param value - The caught value to test
52
+ * @returns True if `value` is a {@link QualifierError} instance; false otherwise
53
+ */
44
54
  function isQualifierError(value) {
45
55
  return value instanceof QualifierError;
46
56
  }
47
57
  //#endregion
48
58
  //#region src/core/validators.ts
49
- /** Determine whether a value is an {@link Eligibility} literal. */
59
+ /**
60
+ * Determines whether a value is an {@link Eligibility} literal.
61
+ *
62
+ * @param value - The value to test
63
+ * @returns True if `value` is one of the eligibility literals; false otherwise
64
+ */
50
65
  var isEligibility = literalOf("eligible", "ineligible", "referral");
51
- /** Determine whether a value is a {@link QualificationEffect} literal. */
66
+ /**
67
+ * Determines whether a value is a {@link QualificationEffect} literal.
68
+ *
69
+ * @param value - The value to test
70
+ * @returns True if `value` is one of the effect literals; false otherwise
71
+ */
52
72
  var isQualificationEffect = literalOf("restriction", "referral", "condition");
53
73
  /**
54
- * Determine whether a value is an open string-keyed record of {@link Eligibility} values.
74
+ * Determines whether a value is an open string-keyed record of {@link Eligibility} values.
55
75
  *
56
76
  * @remarks
57
77
  * Every own string-named property is checked, including non-enumerable properties. Inherited and
58
78
  * symbol-named members are left unchecked because they are outside the record this guard certifies.
59
79
  *
60
80
  * @param value - The value to test
61
- * @returns `true` when `value` is a non-array object whose own string-named values are eligibilities
81
+ * @returns True if `value` is a non-array object whose own string-named values are
82
+ * eligibilities; false otherwise
62
83
  */
63
84
  function isEligibilityRecord(value) {
64
85
  return whereOf(objectOf({}), (record) => Object.getOwnPropertyNames(record).every((key) => isEligibility(Reflect.get(record, key))))(value);
65
86
  }
66
87
  /**
67
- * Determine whether a value is an open result-side {@link Premise}.
88
+ * Determines whether a value is an open result-side {@link Premise}.
68
89
  *
69
90
  * @remarks
70
91
  * `expected` is unchecked and may be absent or contain any value because its published type is
@@ -72,7 +93,7 @@ function isEligibilityRecord(value) {
72
93
  * Unknown members are admitted.
73
94
  *
74
95
  * @param value - The value to test
75
- * @returns `true` when every checked premise member follows its published type
96
+ * @returns True if every checked premise member follows its published type; false otherwise
76
97
  */
77
98
  function isPremise(value) {
78
99
  return objectOf({
@@ -90,14 +111,14 @@ function isPremise(value) {
90
111
  ])(value);
91
112
  }
92
113
  /**
93
- * Determine whether a value is an open result-side {@link Finding}.
114
+ * Determines whether a value is an open result-side {@link Finding}.
94
115
  *
95
116
  * @remarks
96
117
  * Unknown members are admitted. Optional `scope` and `message` members may be absent or
97
118
  * `undefined`; a present defined value must be a string.
98
119
  *
99
120
  * @param value - The value to test
100
- * @returns `true` when every published finding member is valid
121
+ * @returns True if every published finding member is valid; false otherwise
101
122
  */
102
123
  function isFinding(value) {
103
124
  return objectOf({
@@ -112,14 +133,14 @@ function isFinding(value) {
112
133
  }, ["scope", "message"])(value);
113
134
  }
114
135
  /**
115
- * Determine whether a value is an open result-side {@link Derivation}.
136
+ * Determines whether a value is an open result-side {@link Derivation}.
116
137
  *
117
138
  * @remarks
118
139
  * Unknown members are admitted. `value` follows the published `number` type, including `NaN` and
119
140
  * infinities.
120
141
  *
121
142
  * @param value - The value to test
122
- * @returns `true` when every published derivation member is valid
143
+ * @returns True if every published derivation member is valid; false otherwise
123
144
  */
124
145
  function isDerivation(value) {
125
146
  return objectOf({
@@ -131,14 +152,14 @@ function isDerivation(value) {
131
152
  })(value);
132
153
  }
133
154
  /**
134
- * Determine whether a value is an open {@link QualificationResult} returned by a qualifier.
155
+ * Determines whether a value is an open {@link QualificationResult} returned by a qualifier.
135
156
  *
136
157
  * @remarks
137
158
  * This guard is result-postured for values returned through a borrowed qualifier interface. It
138
159
  * admits unknown members and class instances while checking the complete published result closure.
139
160
  *
140
161
  * @param value - The value to test
141
- * @returns `true` when every published qualification-result member is valid
162
+ * @returns True if every published qualification-result member is valid; false otherwise
142
163
  */
143
164
  function isQualificationResult(value) {
144
165
  return objectOf({
@@ -154,23 +175,20 @@ function isQualificationResult(value) {
154
175
  })(value);
155
176
  }
156
177
  /**
157
- * Determine whether a value is an open {@link QualificationValidationResult} returned by a qualifier.
158
- *
159
- * @remarks
160
- * `QualificationValidationResult` is an alias of reasons' validation-result type, so this IS
161
- * reasons' published open result guard by delegation — the alias tracks reasons' shape
162
- * automatically, and a hand-written member list here would silently stop checking a member a
163
- * later reason release adds.
178
+ * Determines whether a value is a {@link QualificationPass} (a quantitative or logical definition).
164
179
  *
165
180
  * @param value - The value to test
166
- * @returns `true` when every published qualification-validation-result member is valid
181
+ * @returns True if `value` is a complete reason quantitative or logical definition; false otherwise
167
182
  */
168
- var isQualificationValidationResult = isReasonValidationResult;
169
- /** Determine whether a value is a {@link QualificationPass} (a quantitative or logical definition). */
170
183
  function isQualificationPass(value) {
171
184
  return isQuantitativeDefinition(value) || isLogicalDefinition(value);
172
185
  }
173
- /** Determine whether a value is an exact {@link Ruling} record. */
186
+ /**
187
+ * Determines whether a value is an exact {@link Ruling} record.
188
+ *
189
+ * @param value - The value to test
190
+ * @returns True if `value` carries every ruling member and no unknown key; false otherwise
191
+ */
174
192
  function isRuling(value) {
175
193
  return recordOf({
176
194
  id: isString,
@@ -181,7 +199,12 @@ function isRuling(value) {
181
199
  message: isString
182
200
  }, ["scope", "message"])(value);
183
201
  }
184
- /** Determine whether a value is an exact {@link QualificationDefinition} record. */
202
+ /**
203
+ * Determines whether a value is an exact {@link QualificationDefinition} record.
204
+ *
205
+ * @param value - The value to test
206
+ * @returns True if `value` carries every definition member and no unknown key; false otherwise
207
+ */
185
208
  function isQualificationDefinition(value) {
186
209
  return recordOf({
187
210
  id: isString,
@@ -199,7 +222,7 @@ function isQualificationDefinition(value) {
199
222
  //#endregion
200
223
  //#region src/core/helpers.ts
201
224
  /**
202
- * Interpolate `{{dotted.path}}` tokens in a message template against a subject.
225
+ * Interpolates `{{dotted.path}}` tokens in a message template against a subject.
203
226
  *
204
227
  * @remarks
205
228
  * Each token is split on `.` into a `FieldPath` array and resolved with
@@ -229,19 +252,19 @@ function interpolateMessage(template, subject) {
229
252
  });
230
253
  }
231
254
  /**
232
- * Describe a {@link Premise} comparison as a display-neutral verb phrase.
255
+ * Renders a {@link Premise} comparison as a display-neutral verb phrase.
233
256
  *
234
257
  * @param comparison - The comparison to describe
235
258
  * @returns A display-neutral phrase
236
259
  *
237
260
  * @example
238
261
  * ```ts
239
- * import { describeComparison } from '@orkestrel/qualifier'
262
+ * import { renderComparison } from '@orkestrel/qualifier'
240
263
  *
241
- * describeComparison('above') // 'is more than'
264
+ * renderComparison('above') // 'is more than'
242
265
  * ```
243
266
  */
244
- function describeComparison(comparison) {
267
+ function renderComparison(comparison) {
245
268
  switch (comparison) {
246
269
  case "equals": return "is";
247
270
  case "not": return "is not";
@@ -256,7 +279,7 @@ function describeComparison(comparison) {
256
279
  }
257
280
  }
258
281
  /**
259
- * Render a structured or scalar expected value display-neutrally.
282
+ * Renders a structured or scalar expected value display-neutrally.
260
283
  *
261
284
  * @remarks
262
285
  * An array renders as its elements, each `String`-coerced, joined with `', '`.
@@ -271,14 +294,14 @@ function describeComparison(comparison) {
271
294
  *
272
295
  * @example
273
296
  * ```ts
274
- * import { describeValue } from '@orkestrel/qualifier'
297
+ * import { renderValue } from '@orkestrel/qualifier'
275
298
  *
276
- * describeValue([18, 25, 40]) // '18, 25, 40'
277
- * describeValue({ minimum: 18, maximum: 65 }) // '18 and 65'
278
- * describeValue(42) // '42'
299
+ * renderValue([18, 25, 40]) // '18, 25, 40'
300
+ * renderValue({ minimum: 18, maximum: 65 }) // '18 and 65'
301
+ * renderValue(42) // '42'
279
302
  * ```
280
303
  */
281
- function describeValue(value) {
304
+ function renderValue(value) {
282
305
  if (Array.isArray(value)) return value.map((entry) => String(entry)).join(", ");
283
306
  if (isBounds(value)) {
284
307
  const sides = [];
@@ -289,7 +312,7 @@ function describeValue(value) {
289
312
  return String(value);
290
313
  }
291
314
  /**
292
- * Render one {@link Premise} into a display-neutral sentence.
315
+ * Renders one {@link Premise} into a display-neutral sentence.
293
316
  *
294
317
  * @remarks
295
318
  * The checked form renders only when `field` and `comparison` are BOTH
@@ -302,25 +325,25 @@ function describeValue(value) {
302
325
  *
303
326
  * @example
304
327
  * ```ts
305
- * import { describePremise } from '@orkestrel/qualifier'
328
+ * import { renderPremise } from '@orkestrel/qualifier'
306
329
  *
307
- * describePremise({ field: 'age', comparison: 'above', expected: 18, actual: 25, met: true })
330
+ * renderPremise({ field: 'age', comparison: 'above', expected: 18, actual: 25, met: true })
308
331
  * // 'age is more than 18 → met'
309
332
  *
310
- * describePremise({ description: 'Applicant is enrolled', met: true })
333
+ * renderPremise({ description: 'Applicant is enrolled', met: true })
311
334
  * // 'Applicant is enrolled → met'
312
335
  * ```
313
336
  */
314
- function describePremise(entry, labels) {
337
+ function renderPremise(entry, labels) {
315
338
  const status = entry.met === void 0 ? "unknown" : entry.met ? "met" : "not met";
316
339
  if (entry.field === void 0 || entry.comparison === void 0) return `${entry.description ?? "Premise"} → ${status}`;
317
340
  const field = formatField(entry.field);
318
341
  const label = labels?.[field] ?? entry.label ?? field;
319
- const expected = entry.expected === void 0 ? "" : ` ${describeValue(entry.expected)}`;
320
- return `${label} ${describeComparison(entry.comparison)}${expected} → ${status}`;
342
+ const expected = entry.expected === void 0 ? "" : ` ${renderValue(entry.expected)}`;
343
+ return `${label} ${renderComparison(entry.comparison)}${expected} → ${status}`;
321
344
  }
322
345
  /**
323
- * Build a {@link Premise} from an authored {@link Check} and its evaluated
346
+ * Builds a {@link Premise} from an authored {@link Check} and its evaluated
324
347
  * `CheckResult`.
325
348
  *
326
349
  * @param check - The authored check
@@ -330,15 +353,15 @@ function describePremise(entry, labels) {
330
353
  *
331
354
  * @example
332
355
  * ```ts
333
- * import { premiseCheck } from '@orkestrel/qualifier'
334
- * import { check } from '@orkestrel/reason'
356
+ * import { checkToPremise } from '@orkestrel/qualifier'
357
+ * import { createCheck } from '@orkestrel/reason'
335
358
  *
336
- * const authored = check('age', 'above', 18)
337
- * premiseCheck(authored, { field: 'age', actual: 25, met: true })
359
+ * const authored = createCheck('age', 'above', 18)
360
+ * checkToPremise(authored, { field: 'age', actual: 25, met: true })
338
361
  * // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
339
362
  * ```
340
363
  */
341
- function premiseCheck(check, result, labels) {
364
+ function checkToPremise(check, result, labels) {
342
365
  const field = formatField(check.field);
343
366
  return {
344
367
  field: check.field,
@@ -350,13 +373,13 @@ function premiseCheck(check, result, labels) {
350
373
  };
351
374
  }
352
375
  /**
353
- * Build rich premises for one fired {@link Rule} by walking its premise atoms
376
+ * Builds rich premises for one fired {@link Rule} by walking its premise atoms
354
377
  * and re-evaluating each against the working subject.
355
378
  *
356
379
  * @remarks
357
380
  * A reason rule result carries only booleans, so this is the qualifier's own
358
381
  * premise-audit projection: each authored premise expression is flattened to its
359
- * atom leaves via `extractAtoms`, and each leaf's `Check` is re-evaluated through
382
+ * atom leaves through `extractAtoms`, and each leaf's `Check` is re-evaluated through
360
383
  * the injected `evaluator`. A membership check (`any` / `none`) over an EMPTY
361
384
  * array value is content-free and is skipped.
362
385
  *
@@ -368,29 +391,29 @@ function premiseCheck(check, result, labels) {
368
391
  *
369
392
  * @example
370
393
  * ```ts
371
- * import { logicalPremises } from '@orkestrel/qualifier'
372
- * import { atom, createEvaluator, logicalDefinition, rule } from '@orkestrel/reason'
394
+ * import { ruleToPremises } from '@orkestrel/qualifier'
395
+ * import { createAtom, createEvaluator, createLogicalDefinition, createRule } from '@orkestrel/reason'
373
396
  *
374
- * const gates = logicalDefinition('gates', 'Gates', [
375
- * rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
397
+ * const gates = createLogicalDefinition('gates', 'Gates', [
398
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
376
399
  * ])
377
400
  * const evaluator = createEvaluator()
378
- * const premises = logicalPremises(gates.rules[0], { licensed: false }, evaluator)
401
+ * const premises = ruleToPremises(gates.rules[0], { licensed: false }, evaluator)
379
402
  *
380
403
  * premises[0]?.met // true
381
404
  * ```
382
405
  */
383
- function logicalPremises(rule, working, evaluator, labels) {
406
+ function ruleToPremises(rule, working, evaluator, labels) {
384
407
  const output = [];
385
408
  for (const premise of rule.premises) for (const atom of extractAtoms(premise)) {
386
409
  const { check } = atom;
387
410
  if ((check.operator === "any" || check.operator === "none") && Array.isArray(check.value) && check.value.length === 0) continue;
388
- output.push(premiseCheck(check, evaluator.evaluate(check, working), labels));
411
+ output.push(checkToPremise(check, evaluator.evaluate(check, working), labels));
389
412
  }
390
413
  return output;
391
414
  }
392
415
  /**
393
- * Locate an authored {@link Rule} by id.
416
+ * Locates an authored {@link Rule} by id.
394
417
  *
395
418
  * @param definition - The logical definition to search
396
419
  * @param id - The rule id
@@ -399,10 +422,10 @@ function logicalPremises(rule, working, evaluator, labels) {
399
422
  * @example
400
423
  * ```ts
401
424
  * import { findRule } from '@orkestrel/qualifier'
402
- * import { atom, logicalDefinition, rule } from '@orkestrel/reason'
425
+ * import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
403
426
  *
404
- * const gates = logicalDefinition('gates', 'Gates', [
405
- * rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
427
+ * const gates = createLogicalDefinition('gates', 'Gates', [
428
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
406
429
  * ])
407
430
  *
408
431
  * findRule(gates, 'licensed')?.id // 'licensed'
@@ -412,7 +435,7 @@ function findRule(definition, id) {
412
435
  return definition.rules.find((rule) => rule.id === id);
413
436
  }
414
437
  /**
415
- * Project one reason result into the internal qualification namespace.
438
+ * Projects one reason result into the internal qualification namespace.
416
439
  *
417
440
  * @remarks
418
441
  * A quantitative result contributes its numeric `value`. A logical result
@@ -427,10 +450,10 @@ function findRule(definition, id) {
427
450
  * @example
428
451
  * ```ts
429
452
  * import { reasonResultToProjection } from '@orkestrel/qualifier'
430
- * import { factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'
453
+ * import { createFactorGroup, createQuantitativeDefinition, createStaticFactor } from '@orkestrel/reason'
431
454
  *
432
- * const cap = quantitativeDefinition('cap', 'TIV cap', [
433
- * factorGroup('limit', 'sum', [staticFactor('base', 500_000)]),
455
+ * const cap = createQuantitativeDefinition('cap', 'TIV cap', [
456
+ * createFactorGroup('limit', 'sum', [createStaticFactor('base', 500_000)]),
434
457
  * ])
435
458
  * const result = {
436
459
  * reasoning: 'quantitative' as const,
@@ -458,7 +481,7 @@ function reasonResultToProjection(definition, result) {
458
481
  return projection;
459
482
  }
460
483
  /**
461
- * Project a quantitative result into a {@link Derivation} audit record.
484
+ * Projects a quantitative result into a {@link Derivation} audit record.
462
485
  *
463
486
  * @param id - The pass id
464
487
  * @param result - The evaluated quantitative result
@@ -483,7 +506,7 @@ function quantitativeResultToDerivation(id, result) {
483
506
  };
484
507
  }
485
508
  /**
486
- * Wrap a {@link QualificationContext} under {@link QUALIFICATION_KEY}.
509
+ * Wraps a {@link QualificationContext} under {@link QUALIFICATION_KEY}.
487
510
  *
488
511
  * @param context - The accumulated projection context
489
512
  * @returns A record carrying the context under the reserved key
@@ -499,7 +522,7 @@ function qualificationToRecord(context) {
499
522
  return { [QUALIFICATION_KEY]: context };
500
523
  }
501
524
  /**
502
- * Copy-on-write merge one pass projection into the context.
525
+ * Merges one pass projection into the context, copy-on-write.
503
526
  *
504
527
  * @param context - The current projection context
505
528
  * @param id - The pass id to project under
@@ -521,7 +544,7 @@ function mergeQualificationContext(context, id, projection) {
521
544
  };
522
545
  }
523
546
  /**
524
- * Join a ruling, its logical rule result, the pass, the pre-projection subject,
547
+ * Joins a ruling, its logical rule result, the pass, the pre-projection subject,
525
548
  * and an evaluator into a {@link Finding}.
526
549
  *
527
550
  * @remarks
@@ -541,22 +564,22 @@ function mergeQualificationContext(context, id, projection) {
541
564
  * @example
542
565
  * ```ts
543
566
  * import { rulingToFinding } from '@orkestrel/qualifier'
544
- * import { atom, createEvaluator, logicalDefinition, rule } from '@orkestrel/reason'
567
+ * import { createAtom, createEvaluator, createLogicalDefinition, createRule } from '@orkestrel/reason'
545
568
  *
546
- * const gates = logicalDefinition('gates', 'Gates', [
547
- * rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
569
+ * const gates = createLogicalDefinition('gates', 'Gates', [
570
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
548
571
  * ])
549
572
  * const ruling = { id: 'license', pass: 'gates', rule: 'licensed', effect: 'restriction' as const }
550
- * const result = { reasoning: 'logical' as const, conclusion: true, rules: [{ id: 'licensed', applied: true, premises: [true], conclusion: true }], count: 1, success: true, trace: [], errors: [] }
573
+ * const result = { reasoning: 'logical' as const, conclusion: true, rules: [{ id: 'licensed', applied: true, premises: [true] }], count: 1, success: true, trace: [], errors: [] }
551
574
  * const finding = rulingToFinding(ruling, gates, result, { licensed: false }, createEvaluator())
552
575
  *
553
576
  * finding.applied // true
554
577
  * ```
555
578
  */
556
579
  function rulingToFinding(ruling, pass, result, subject, evaluator, labels) {
557
- const applied = result.rules.find((item) => item.id === ruling.rule)?.applied ?? false;
580
+ const applied = result.rules.find((resolved) => resolved.id === ruling.rule)?.applied ?? false;
558
581
  const rule = pass.reasoning === "logical" ? findRule(pass, ruling.rule) : void 0;
559
- const premises = rule === void 0 ? [] : logicalPremises(rule, subject, evaluator, labels);
582
+ const premises = rule === void 0 ? [] : ruleToPremises(rule, subject, evaluator, labels);
560
583
  return {
561
584
  id: ruling.id,
562
585
  pass: ruling.pass,
@@ -569,7 +592,7 @@ function rulingToFinding(ruling, pass, result, subject, evaluator, labels) {
569
592
  };
570
593
  }
571
594
  /**
572
- * Derive global eligibility from applied, unscoped findings.
595
+ * Derives global eligibility from applied, unscoped findings.
573
596
  *
574
597
  * @remarks
575
598
  * Only findings that both applied and carry no `scope` shape global eligibility.
@@ -577,7 +600,8 @@ function rulingToFinding(ruling, pass, result, subject, evaluator, labels) {
577
600
  * `failed`.
578
601
  *
579
602
  * @param findings - The resolved findings
580
- * @param failed - Whether an operational pass failure occurred
603
+ * @param failed - If `true`, adds a synthetic `referral` impact so an operational failure
604
+ * fails closed; if `false`, derives eligibility from the findings alone. Default: `false`
581
605
  * @returns The most severe global eligibility
582
606
  *
583
607
  * @example
@@ -594,7 +618,7 @@ function deriveFindingEligibility(findings, failed = false) {
594
618
  return combineEligibilities(eligibilities);
595
619
  }
596
620
  /**
597
- * Return the most severe {@link Eligibility} in a list.
621
+ * Returns the most severe {@link Eligibility} in a list.
598
622
  *
599
623
  * @param eligibilities - The eligibilities to combine
600
624
  * @returns The most severe eligibility, or `'eligible'` for an empty list
@@ -611,7 +635,7 @@ function combineEligibilities(eligibilities) {
611
635
  return "eligible";
612
636
  }
613
637
  /**
614
- * Derive one eligibility per finding scope.
638
+ * Derives one eligibility per finding scope.
615
639
  *
616
640
  * @remarks
617
641
  * Only applied, scoped findings contribute. Each named scope combines its own
@@ -641,29 +665,29 @@ function deriveScopeEligibilities(findings) {
641
665
  return scopes;
642
666
  }
643
667
  /**
644
- * Find rulings whose pass or rule does not exist, whose pass is not logical, or
645
- * a pass id shadowing the reserved {@link QUALIFICATION_KEY}.
668
+ * Describes each ruling whose pass or rule does not exist or whose pass is not
669
+ * logical, and each pass id shadowing the reserved {@link QUALIFICATION_KEY}.
646
670
  *
647
671
  * @param definition - The qualification definition to check
648
672
  * @returns A fresh list of reference-error messages
649
673
  *
650
674
  * @example
651
675
  * ```ts
652
- * import { findMissingReferences, qualificationDefinition, rulingDefinition } from '@orkestrel/qualifier'
653
- * import { atom, logicalDefinition, rule } from '@orkestrel/reason'
676
+ * import { createQualificationDefinition, createRuling, describeMissingReferences } from '@orkestrel/qualifier'
677
+ * import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
654
678
  *
655
- * const gates = logicalDefinition('gates', 'Gates', [
656
- * rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
679
+ * const gates = createLogicalDefinition('gates', 'Gates', [
680
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
657
681
  * ])
658
- * const definition = qualificationDefinition('standard', 'Standard', [gates], {
659
- * rulings: [rulingDefinition('license', 'gates', 'absent', 'restriction')],
682
+ * const definition = createQualificationDefinition('standard', 'Standard', [gates], {
683
+ * rulings: [createRuling('license', 'gates', 'absent', 'restriction')],
660
684
  * })
661
685
  *
662
- * findMissingReferences(definition)
686
+ * describeMissingReferences(definition)
663
687
  * // ["Ruling 'license' references missing rule 'absent' in pass 'gates'"]
664
688
  * ```
665
689
  */
666
- function findMissingReferences(definition) {
690
+ function describeMissingReferences(definition) {
667
691
  const errors = [];
668
692
  const passes = new Map(definition.passes.map((pass) => [pass.id, pass]));
669
693
  for (const pass of definition.passes) if (pass.id === "qualification") errors.push(`Pass id must not equal reserved key '${QUALIFICATION_KEY}'`);
@@ -682,10 +706,10 @@ function findMissingReferences(definition) {
682
706
  return errors;
683
707
  }
684
708
  /**
685
- * Determine whether a subject already owns the reserved {@link QUALIFICATION_KEY}.
709
+ * Determines whether a subject already owns the reserved {@link QUALIFICATION_KEY}.
686
710
  *
687
711
  * @param subject - The subject to check
688
- * @returns `true` when the subject owns the reserved key
712
+ * @returns True if the subject owns the reserved key; false otherwise
689
713
  *
690
714
  * @example
691
715
  * ```ts
@@ -698,7 +722,7 @@ function hasReservedKey(subject) {
698
722
  return Object.hasOwn(subject, QUALIFICATION_KEY);
699
723
  }
700
724
  /**
701
- * Assert a value is a valid qualification {@link Subject}, narrowing it in place.
725
+ * Asserts a value is a valid qualification {@link Subject}, narrowing it in place.
702
726
  *
703
727
  * @param value - The candidate subject to validate
704
728
  * @throws {@link QualifierError} `'MISMATCH'` when the value is not a record, or
@@ -716,12 +740,12 @@ function assertSubject(value) {
716
740
  if (hasReservedKey(value)) throw new QualifierError("MISMATCH", `Qualification subject must not contain reserved key '${QUALIFICATION_KEY}'`);
717
741
  }
718
742
  /**
719
- * Map an engine throw caught while running one pass to a typed {@link QualifierError}.
743
+ * Maps an engine throw caught while running one pass to a typed {@link QualifierError}.
720
744
  *
721
745
  * @remarks
722
746
  * A reason `ReasonError('INVALID')` maps to `DEFINITION` (the engine rejected the
723
747
  * pass as an invalid definition); `ReasonError('DESTROYED')` maps to `DESTROYED`;
724
- * every other `ReasonError` code (e.g. `MISSING`) and every non-`ReasonError` throw
748
+ * every other `ReasonError` code (for example `MISSING`) and every non-`ReasonError` throw
725
749
  * (including a `bail: true` reasoner rethrow) maps to `ENGINE`. The original throw
726
750
  * is preserved as `context.cause`.
727
751
  *
@@ -758,7 +782,7 @@ function mapEngineError(error, pass) {
758
782
  });
759
783
  }
760
784
  /**
761
- * Find logical passes carrying no rulings.
785
+ * Describes each logical pass carrying no rulings.
762
786
  *
763
787
  * @remarks
764
788
  * A logical pass with no matching ruling can never contribute a finding, so its
@@ -770,18 +794,18 @@ function mapEngineError(error, pass) {
770
794
  *
771
795
  * @example
772
796
  * ```ts
773
- * import { findEmptyLogicalPasses, qualificationDefinition } from '@orkestrel/qualifier'
774
- * import { atom, logicalDefinition, rule } from '@orkestrel/reason'
797
+ * import { createQualificationDefinition, describeEmptyLogicalPasses } from '@orkestrel/qualifier'
798
+ * import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
775
799
  *
776
- * const gates = logicalDefinition('gates', 'Gates', [
777
- * rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
800
+ * const gates = createLogicalDefinition('gates', 'Gates', [
801
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
778
802
  * ])
779
- * const definition = qualificationDefinition('standard', 'Standard', [gates])
803
+ * const definition = createQualificationDefinition('standard', 'Standard', [gates])
780
804
  *
781
- * findEmptyLogicalPasses(definition) // ["Logical pass 'gates' has no rulings"]
805
+ * describeEmptyLogicalPasses(definition) // ["Logical pass 'gates' has no rulings"]
782
806
  * ```
783
807
  */
784
- function findEmptyLogicalPasses(definition) {
808
+ function describeEmptyLogicalPasses(definition) {
785
809
  const warnings = [];
786
810
  for (const pass of definition.passes) {
787
811
  if (pass.reasoning !== "logical") continue;
@@ -790,7 +814,7 @@ function findEmptyLogicalPasses(definition) {
790
814
  return warnings;
791
815
  }
792
816
  /**
793
- * Find quantitative passes never read by a later pass.
817
+ * Describes each quantitative pass never read by a later pass.
794
818
  *
795
819
  * @remarks
796
820
  * A quantitative pass's projected value lives under `qualification.<id>` — if no
@@ -804,18 +828,18 @@ function findEmptyLogicalPasses(definition) {
804
828
  *
805
829
  * @example
806
830
  * ```ts
807
- * import { findUnreadDerivations, qualificationDefinition } from '@orkestrel/qualifier'
808
- * import { factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'
831
+ * import { createQualificationDefinition, describeUnreadDerivations } from '@orkestrel/qualifier'
832
+ * import { createFactorGroup, createQuantitativeDefinition, createStaticFactor } from '@orkestrel/reason'
809
833
  *
810
- * const cap = quantitativeDefinition('cap', 'TIV cap', [
811
- * factorGroup('limit', 'sum', [staticFactor('base', 500_000)]),
834
+ * const cap = createQuantitativeDefinition('cap', 'TIV cap', [
835
+ * createFactorGroup('limit', 'sum', [createStaticFactor('base', 500_000)]),
812
836
  * ])
813
- * const definition = qualificationDefinition('standard', 'Standard', [cap])
837
+ * const definition = createQualificationDefinition('standard', 'Standard', [cap])
814
838
  *
815
- * findUnreadDerivations(definition) // ["Quantitative pass 'cap' is never read by a later pass"]
839
+ * describeUnreadDerivations(definition) // ["Quantitative pass 'cap' is never read by a later pass"]
816
840
  * ```
817
841
  */
818
- function findUnreadDerivations(definition) {
842
+ function describeUnreadDerivations(definition) {
819
843
  const warnings = [];
820
844
  definition.passes.forEach((pass, index) => {
821
845
  if (pass.reasoning !== "quantitative") return;
@@ -832,69 +856,10 @@ function findUnreadDerivations(definition) {
832
856
  });
833
857
  return warnings;
834
858
  }
835
- /**
836
- * Build a {@link QualificationDefinition}.
837
- *
838
- * @remarks
839
- * Returns a fresh top-level definition, omitting absent optional keys;
840
- * `passes` and `rulings` arrays are copied, and record `metadata` is
841
- * shallow-copied so nested values are not deep-cloned.
842
- *
843
- * @param id - The definition id
844
- * @param name - The display name
845
- * @param passes - The ordered qualification passes
846
- * @param input - Optional description, rulings, and metadata
847
- * @returns A fresh qualification definition
848
- *
849
- * @example
850
- * ```ts
851
- * import { qualificationDefinition } from '@orkestrel/qualifier'
852
- *
853
- * qualificationDefinition('standard', 'Standard', [gates], { rulings: [ruling] })
854
- * ```
855
- */
856
- function qualificationDefinition(id, name, passes, input) {
857
- return {
858
- id,
859
- name,
860
- passes: [...passes],
861
- ...input?.description === void 0 ? {} : { description: input.description },
862
- ...input?.rulings === void 0 ? {} : { rulings: [...input.rulings] },
863
- ...input?.metadata === void 0 ? {} : { metadata: { ...input.metadata } }
864
- };
865
- }
866
- /**
867
- * Build a {@link Ruling} — one authored consequence for one rule in one pass.
868
- *
869
- * @param id - The ruling id
870
- * @param pass - The logical pass id the rule lives in
871
- * @param rule - The rule id whose firing this ruling reacts to
872
- * @param effect - The eligibility impact when the rule fires
873
- * @param input - Optional selection scope and message template
874
- * @returns A fresh ruling
875
- *
876
- * @example
877
- * ```ts
878
- * import { rulingDefinition } from '@orkestrel/qualifier'
879
- *
880
- * rulingDefinition('license', 'gates', 'licensed', 'restriction', { message: 'A license is required' })
881
- * ```
882
- */
883
- function rulingDefinition(id, pass, rule, effect, input) {
884
- return {
885
- id,
886
- pass,
887
- rule,
888
- effect,
889
- ...input?.scope === void 0 ? {} : { scope: input.scope },
890
- ...input?.message === void 0 ? {} : { message: input.message }
891
- };
892
- }
893
859
  //#endregion
894
860
  //#region src/core/Qualifier.ts
895
861
  /**
896
- * A qualifier — runs ordered passes over one reason engine and returns
897
- * eligibility.
862
+ * Runs ordered passes over one reason engine and returns eligibility.
898
863
  *
899
864
  * @remarks
900
865
  * The engine is OWNED when self-created (destroyed on `destroy()`) and borrowed
@@ -924,17 +889,92 @@ var Qualifier = class {
924
889
  this.#validate = options?.validate ?? true;
925
890
  this.#labels = options?.labels;
926
891
  }
892
+ /**
893
+ * Holds the typed observation surface carrying `derive`, `finding`, `qualify`, and
894
+ * `destroy`.
895
+ *
896
+ * @returns The emitter this qualifier owns
897
+ *
898
+ * @example
899
+ * ```ts
900
+ * import { Qualifier } from '@orkestrel/qualifier'
901
+ *
902
+ * const qualifier = new Qualifier()
903
+ * qualifier.emitter.on('qualify', (result) => console.log(result.eligibility))
904
+ * qualifier.destroy()
905
+ * ```
906
+ */
927
907
  get emitter() {
928
908
  return this.#emitter;
929
909
  }
910
+ /**
911
+ * Qualifies one subject against one authored definition.
912
+ *
913
+ * @remarks
914
+ * Semantic validation runs first when the `validate` option is on, so an invalid
915
+ * definition throws before any pass runs. Passes run in authored order over a
916
+ * copy-on-write working subject, and the caller's `subject` is never mutated. An
917
+ * unscoped restriction stops the remaining passes because no later finding can be
918
+ * more severe.
919
+ *
920
+ * @param subject - The record to qualify
921
+ * @param definition - The authored qualification definition
922
+ * @returns A fresh, frozen qualification result
923
+ * @throws {@link QualifierError} `'DEFINITION'` when validation is enabled and the
924
+ * definition fails semantic validation, or when the engine rejects a pass as an invalid
925
+ * definition; `'MISMATCH'` when the subject is not a record or already carries the
926
+ * reserved `qualification` key; `'DESTROYED'` after `destroy`, or when the engine
927
+ * reports itself destroyed mid-pass; `'ENGINE'` for every other engine throw
928
+ *
929
+ * @example
930
+ * ```ts
931
+ * import { createQualificationDefinition, createRuling, Qualifier } from '@orkestrel/qualifier'
932
+ * import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
933
+ *
934
+ * const gates = createLogicalDefinition('gates', 'Eligibility gates', [
935
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
936
+ * ])
937
+ * const definition = createQualificationDefinition('standard', 'Standard eligibility', [gates], {
938
+ * rulings: [createRuling('license', 'gates', 'licensed', 'restriction')],
939
+ * })
940
+ *
941
+ * const qualifier = new Qualifier()
942
+ * qualifier.qualify({ id: 'risk-1', licensed: false }, definition).eligibility // 'ineligible'
943
+ * qualifier.destroy()
944
+ * ```
945
+ */
930
946
  qualify(subject, definition) {
931
947
  this.#alive();
932
948
  if (this.#validate) {
933
949
  const validation = this.validate(definition);
934
- if (!validation.valid) throw new QualifierError("DEFINITION", `Qualification definition is invalid: ${validation.errors.join(", ")}`, definition.id);
950
+ if (!validation.valid) throw new QualifierError("DEFINITION", `Qualification definition is invalid: ${validation.errors.join(", ")}`, { definition: definition.id });
935
951
  }
936
952
  return this.#qualify(subject, definition);
937
953
  }
954
+ /**
955
+ * Validates one authored definition semantically, without running it.
956
+ *
957
+ * @remarks
958
+ * Structural shape is `isQualificationDefinition`'s job. This checks the meaning:
959
+ * non-empty id and name, valid and uniquely identified passes and rulings, every
960
+ * ruling reference resolving to a logical pass and one of its rules, and no pass id
961
+ * shadowing `QUALIFICATION_KEY`. An empty definition, a logical pass carrying no
962
+ * ruling, and a derivation no later pass reads are warnings rather than errors.
963
+ *
964
+ * @param definition - The authored qualification definition
965
+ * @returns A fresh validation result carrying `valid`, `errors`, and `warnings`
966
+ * @throws {@link QualifierError} `'DESTROYED'` after `destroy`
967
+ *
968
+ * @example
969
+ * ```ts
970
+ * import { createQualificationDefinition, Qualifier } from '@orkestrel/qualifier'
971
+ *
972
+ * const qualifier = new Qualifier()
973
+ * qualifier.validate(createQualificationDefinition('empty', 'Empty', []))
974
+ * // { valid: true, errors: [], warnings: ['Definition has no passes'] }
975
+ * qualifier.destroy()
976
+ * ```
977
+ */
938
978
  validate(definition) {
939
979
  this.#alive();
940
980
  const errors = [];
@@ -949,16 +989,33 @@ var Qualifier = class {
949
989
  });
950
990
  for (const id of findDuplicates(definition.passes)) errors.push(`Duplicate pass id '${id}'`);
951
991
  for (const id of findDuplicates(definition.rulings ?? [])) errors.push(`Duplicate ruling id '${id}'`);
952
- for (const reference of findMissingReferences(definition)) errors.push(reference);
992
+ for (const reference of describeMissingReferences(definition)) errors.push(reference);
953
993
  if (definition.passes.length === 0) warnings.push("Definition has no passes");
954
- for (const warning of findEmptyLogicalPasses(definition)) warnings.push(warning);
955
- for (const warning of findUnreadDerivations(definition)) warnings.push(warning);
994
+ for (const warning of describeEmptyLogicalPasses(definition)) warnings.push(warning);
995
+ for (const warning of describeUnreadDerivations(definition)) warnings.push(warning);
956
996
  return {
957
997
  valid: errors.length === 0,
958
998
  errors,
959
999
  warnings
960
1000
  };
961
1001
  }
1002
+ /**
1003
+ * Destroys this qualifier, idempotently.
1004
+ *
1005
+ * @remarks
1006
+ * An engine this qualifier created is destroyed; an injected engine stays
1007
+ * caller-owned and is left alone. The `destroy` event fires before the emitter
1008
+ * itself is destroyed, and a later call returns without re-emitting.
1009
+ *
1010
+ * @example
1011
+ * ```ts
1012
+ * import { Qualifier } from '@orkestrel/qualifier'
1013
+ *
1014
+ * const qualifier = new Qualifier()
1015
+ * qualifier.destroy()
1016
+ * qualifier.destroy() // idempotent
1017
+ * ```
1018
+ */
962
1019
  destroy() {
963
1020
  if (this.#destroyed) return;
964
1021
  if (this.#owned) this.#engine.destroy();
@@ -1034,12 +1091,15 @@ var Qualifier = class {
1034
1091
  //#endregion
1035
1092
  //#region src/core/factories.ts
1036
1093
  /**
1037
- * Create one qualifier over a reason engine.
1094
+ * Creates one qualifier over a reason engine.
1038
1095
  *
1039
1096
  * @remarks
1040
1097
  * A standalone qualifier creates and OWNS one shared quantitative-plus-logical
1041
1098
  * reason engine, destroying it on `destroy()`. An injected `options.engine`
1042
- * remains caller-owned and is never destroyed. Validation is on by default.
1099
+ * remains caller-owned and is never destroyed. `validate` runs semantic
1100
+ * validation before every qualification. Default: `true`. `labels` overrides the
1101
+ * display name of a premise field, keyed by the dot-joined field path
1102
+ * `formatField` produces, and takes precedence over a premise's own `label`.
1043
1103
  *
1044
1104
  * @param options - Optional injected engine, validation, labels, and emitter hooks
1045
1105
  * @returns A {@link QualifierInterface}
@@ -1056,7 +1116,65 @@ var Qualifier = class {
1056
1116
  function createQualifier(options) {
1057
1117
  return new Qualifier(options);
1058
1118
  }
1119
+ /**
1120
+ * Creates a {@link QualificationDefinition}.
1121
+ *
1122
+ * @remarks
1123
+ * Returns a fresh top-level definition, omitting absent optional keys;
1124
+ * `passes` and `rulings` arrays are copied, and record `metadata` is
1125
+ * shallow-copied so nested values are not deep-cloned.
1126
+ *
1127
+ * @param id - The definition id
1128
+ * @param name - The display name
1129
+ * @param passes - The ordered qualification passes
1130
+ * @param input - Optional description, rulings, and metadata
1131
+ * @returns A fresh qualification definition
1132
+ *
1133
+ * @example
1134
+ * ```ts
1135
+ * import { createQualificationDefinition } from '@orkestrel/qualifier'
1136
+ *
1137
+ * createQualificationDefinition('standard', 'Standard', [gates], { rulings: [ruling] })
1138
+ * ```
1139
+ */
1140
+ function createQualificationDefinition(id, name, passes, input) {
1141
+ return {
1142
+ id,
1143
+ name,
1144
+ passes: [...passes],
1145
+ ...input?.description === void 0 ? {} : { description: input.description },
1146
+ ...input?.rulings === void 0 ? {} : { rulings: [...input.rulings] },
1147
+ ...input?.metadata === void 0 ? {} : { metadata: { ...input.metadata } }
1148
+ };
1149
+ }
1150
+ /**
1151
+ * Creates a {@link Ruling} — one authored consequence for one rule in one pass.
1152
+ *
1153
+ * @param id - The ruling id
1154
+ * @param pass - The logical pass id the rule lives in
1155
+ * @param rule - The rule id whose firing this ruling reacts to
1156
+ * @param effect - The eligibility impact when the rule fires
1157
+ * @param input - Optional selection scope and message template
1158
+ * @returns A fresh ruling
1159
+ *
1160
+ * @example
1161
+ * ```ts
1162
+ * import { createRuling } from '@orkestrel/qualifier'
1163
+ *
1164
+ * createRuling('license', 'gates', 'licensed', 'restriction', { message: 'A license is required' })
1165
+ * ```
1166
+ */
1167
+ function createRuling(id, pass, rule, effect, input) {
1168
+ return {
1169
+ id,
1170
+ pass,
1171
+ rule,
1172
+ effect,
1173
+ ...input?.scope === void 0 ? {} : { scope: input.scope },
1174
+ ...input?.message === void 0 ? {} : { message: input.message }
1175
+ };
1176
+ }
1059
1177
  //#endregion
1060
- export { DEFAULT_QUALIFIER_VALIDATE, EFFECT_ELIGIBILITIES, ELIGIBILITY_PRECEDENCE, QUALIFICATION_KEY, Qualifier, QualifierError, assertSubject, combineEligibilities, createQualifier, deriveFindingEligibility, deriveScopeEligibilities, describeComparison, describePremise, describeValue, findEmptyLogicalPasses, findMissingReferences, findRule, findUnreadDerivations, hasReservedKey, interpolateMessage, isDerivation, isEligibility, isEligibilityRecord, isFinding, isPremise, isQualificationDefinition, isQualificationEffect, isQualificationPass, isQualificationResult, isQualificationValidationResult, isQualifierError, isRuling, logicalPremises, mapEngineError, mergeQualificationContext, premiseCheck, qualificationDefinition, qualificationToRecord, quantitativeResultToDerivation, reasonResultToProjection, rulingDefinition, rulingToFinding };
1178
+ export { DEFAULT_QUALIFIER_VALIDATE, EFFECT_ELIGIBILITIES, ELIGIBILITY_PRECEDENCE, QUALIFICATION_KEY, Qualifier, QualifierError, assertSubject, checkToPremise, combineEligibilities, createQualificationDefinition, createQualifier, createRuling, deriveFindingEligibility, deriveScopeEligibilities, describeEmptyLogicalPasses, describeMissingReferences, describeUnreadDerivations, findRule, hasReservedKey, interpolateMessage, isDerivation, isEligibility, isEligibilityRecord, isFinding, isPremise, isQualificationDefinition, isQualificationEffect, isQualificationPass, isQualificationResult, isQualifierError, isRuling, mapEngineError, mergeQualificationContext, qualificationToRecord, quantitativeResultToDerivation, reasonResultToProjection, renderComparison, renderPremise, renderValue, ruleToPremises, rulingToFinding };
1061
1179
 
1062
1180
  //# sourceMappingURL=index.js.map