@orkestrel/program 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/core/index.cjs +288 -80
- package/dist/src/core/index.d.cts +158 -0
- package/dist/src/core/index.d.ts +158 -0
- package/dist/src/core/index.js +285 -85
- package/package.json +15 -13
- package/dist/src/core/index.cjs.map +0 -1
- package/dist/src/core/index.js.map +0 -1
package/dist/src/core/index.d.ts
CHANGED
|
@@ -628,6 +628,45 @@ export declare function aggregateSums(subjects: readonly Subject[], fields: read
|
|
|
628
628
|
*/
|
|
629
629
|
export declare function isAggregateDefinition(value: unknown): value is AggregateDefinition;
|
|
630
630
|
|
|
631
|
+
/**
|
|
632
|
+
* Determine whether a value is an open result-side {@link AggregateGroup}.
|
|
633
|
+
*
|
|
634
|
+
* @remarks
|
|
635
|
+
* Unknown members and class instances are admitted. Arrays are refused.
|
|
636
|
+
*
|
|
637
|
+
* @param value - The candidate value
|
|
638
|
+
* @returns `true` when every published aggregate-group member conforms
|
|
639
|
+
*
|
|
640
|
+
* @example
|
|
641
|
+
* ```ts
|
|
642
|
+
* import { isAggregateGroup } from '@orkestrel/program'
|
|
643
|
+
*
|
|
644
|
+
* isAggregateGroup({ key: 'east', count: 1, sums: { premium: 100 } }) // true
|
|
645
|
+
* ```
|
|
646
|
+
*/
|
|
647
|
+
export declare const isAggregateGroup: Guard<AggregateGroup>;
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Determine whether a value is an open {@link AggregateResult}.
|
|
651
|
+
*
|
|
652
|
+
* @remarks
|
|
653
|
+
* This guard is result-postured for values returned through a borrowed
|
|
654
|
+
* {@link ProgramInterface}. It admits unknown members and class instances while
|
|
655
|
+
* checking every nested program result, determination, group, total tally
|
|
656
|
+
* record, and sums record. Arrays are refused.
|
|
657
|
+
*
|
|
658
|
+
* @param value - The candidate value
|
|
659
|
+
* @returns `true` when every published aggregate-result member conforms
|
|
660
|
+
*
|
|
661
|
+
* @example
|
|
662
|
+
* ```ts
|
|
663
|
+
* import { isAggregateResult } from '@orkestrel/program'
|
|
664
|
+
*
|
|
665
|
+
* isAggregateResult(program.execute(subjects)) // true
|
|
666
|
+
* ```
|
|
667
|
+
*/
|
|
668
|
+
export declare const isAggregateResult: Guard<AggregateResult>;
|
|
669
|
+
|
|
631
670
|
/**
|
|
632
671
|
* Determine whether a value is a {@link Decision} literal.
|
|
633
672
|
*
|
|
@@ -643,6 +682,25 @@ export declare function aggregateSums(subjects: readonly Subject[], fields: read
|
|
|
643
682
|
*/
|
|
644
683
|
export declare const isDecision: Guard<Decision>;
|
|
645
684
|
|
|
685
|
+
/**
|
|
686
|
+
* Determine whether a value is an open result-side {@link Determination}.
|
|
687
|
+
*
|
|
688
|
+
* @remarks
|
|
689
|
+
* Unknown members and class instances are admitted. Arrays are refused.
|
|
690
|
+
* Optional `scope` and `message` members may be absent or `undefined`.
|
|
691
|
+
*
|
|
692
|
+
* @param value - The candidate value
|
|
693
|
+
* @returns `true` when every published determination member conforms
|
|
694
|
+
*
|
|
695
|
+
* @example
|
|
696
|
+
* ```ts
|
|
697
|
+
* import { isDetermination } from '@orkestrel/program'
|
|
698
|
+
*
|
|
699
|
+
* isDetermination({ id: 'audit', effect: 'notice', applied: true, premises: [] }) // true
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
702
|
+
export declare const isDetermination: Guard<Determination>;
|
|
703
|
+
|
|
646
704
|
/**
|
|
647
705
|
* Determine whether a value is an exact {@link Notice} record.
|
|
648
706
|
*
|
|
@@ -695,6 +753,69 @@ export declare function aggregateSums(subjects: readonly Subject[], fields: read
|
|
|
695
753
|
/** Narrow a caught value to a {@link ProgramError}. */
|
|
696
754
|
export declare function isProgramError(value: unknown): value is ProgramError;
|
|
697
755
|
|
|
756
|
+
/**
|
|
757
|
+
* Determine whether a value is an open {@link ProgramResult}.
|
|
758
|
+
*
|
|
759
|
+
* @remarks
|
|
760
|
+
* This guard is result-postured for values returned through a borrowed
|
|
761
|
+
* {@link ProgramInterface}. It admits unknown members and class instances while
|
|
762
|
+
* composing qualifier's `isQualificationResult` and rater's `isRatingResult`
|
|
763
|
+
* over their complete nested result closures. Arrays are refused.
|
|
764
|
+
*
|
|
765
|
+
* @param value - The candidate value
|
|
766
|
+
* @returns `true` when every published program-result member conforms
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* ```ts
|
|
770
|
+
* import { isProgramResult } from '@orkestrel/program'
|
|
771
|
+
*
|
|
772
|
+
* isProgramResult(program.execute(subject)) // true
|
|
773
|
+
* ```
|
|
774
|
+
*/
|
|
775
|
+
export declare const isProgramResult: Guard<ProgramResult>;
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Determine whether a value is an open program sums record.
|
|
779
|
+
*
|
|
780
|
+
* @remarks
|
|
781
|
+
* Every own string-named property is checked, including non-enumerable
|
|
782
|
+
* properties. Inherited and symbol-named members are outside the record this
|
|
783
|
+
* guard certifies. Values remain plain JavaScript numbers, including `NaN` and
|
|
784
|
+
* infinities, because the published contract does not refine them.
|
|
785
|
+
*
|
|
786
|
+
* @param value - The candidate value
|
|
787
|
+
* @returns `true` when every own string-named value is a number
|
|
788
|
+
*
|
|
789
|
+
* @example
|
|
790
|
+
* ```ts
|
|
791
|
+
* import { isProgramSums } from '@orkestrel/program'
|
|
792
|
+
*
|
|
793
|
+
* isProgramSums({ premium: 100 }) // true
|
|
794
|
+
* ```
|
|
795
|
+
*/
|
|
796
|
+
export declare function isProgramSums(value: unknown): value is Readonly<Record<string, number>>;
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Determine whether a value is an open {@link ProgramValidationResult}.
|
|
800
|
+
*
|
|
801
|
+
* @remarks
|
|
802
|
+
* `ProgramValidationResult` is this package's own declared interface, not an
|
|
803
|
+
* alias of reason's validation result. This guard therefore checks the three
|
|
804
|
+
* program-owned members directly so the contracts may evolve independently.
|
|
805
|
+
* Unknown members and class instances are admitted. Arrays are refused.
|
|
806
|
+
*
|
|
807
|
+
* @param value - The candidate value
|
|
808
|
+
* @returns `true` when every published program-validation member conforms
|
|
809
|
+
*
|
|
810
|
+
* @example
|
|
811
|
+
* ```ts
|
|
812
|
+
* import { isProgramValidationResult } from '@orkestrel/program'
|
|
813
|
+
*
|
|
814
|
+
* isProgramValidationResult({ valid: true, errors: [], warnings: [] }) // true
|
|
815
|
+
* ```
|
|
816
|
+
*/
|
|
817
|
+
export declare const isProgramValidationResult: Guard<ProgramValidationResult>;
|
|
818
|
+
|
|
698
819
|
/**
|
|
699
820
|
* Determine whether a value is a {@link Status} literal.
|
|
700
821
|
*
|
|
@@ -710,6 +831,43 @@ export declare function aggregateSums(subjects: readonly Subject[], fields: read
|
|
|
710
831
|
*/
|
|
711
832
|
export declare const isStatus: Guard<Status>;
|
|
712
833
|
|
|
834
|
+
/**
|
|
835
|
+
* Determine whether a value is a total open status-tally record.
|
|
836
|
+
*
|
|
837
|
+
* @remarks
|
|
838
|
+
* Every {@link Status} in {@link STATUS_PRECEDENCE} is required and checked.
|
|
839
|
+
* Unknown members and class instances are admitted. Arrays are refused.
|
|
840
|
+
*
|
|
841
|
+
* @param value - The candidate value
|
|
842
|
+
* @returns `true` when every required status member is a {@link Tally}
|
|
843
|
+
*
|
|
844
|
+
* @example
|
|
845
|
+
* ```ts
|
|
846
|
+
* import { emptyTallies, isTallies } from '@orkestrel/program'
|
|
847
|
+
*
|
|
848
|
+
* isTallies(emptyTallies([])) // true
|
|
849
|
+
* ```
|
|
850
|
+
*/
|
|
851
|
+
export declare function isTallies(value: unknown): value is Readonly<Record<Status, Tally>>;
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Determine whether a value is an open result-side {@link Tally}.
|
|
855
|
+
*
|
|
856
|
+
* @remarks
|
|
857
|
+
* Unknown members and class instances are admitted. Arrays are refused.
|
|
858
|
+
*
|
|
859
|
+
* @param value - The candidate value
|
|
860
|
+
* @returns `true` when every published tally member conforms
|
|
861
|
+
*
|
|
862
|
+
* @example
|
|
863
|
+
* ```ts
|
|
864
|
+
* import { isTally } from '@orkestrel/program'
|
|
865
|
+
*
|
|
866
|
+
* isTally({ count: 1, sums: { premium: 100 } }) // true
|
|
867
|
+
* ```
|
|
868
|
+
*/
|
|
869
|
+
export declare const isTally: Guard<Tally>;
|
|
870
|
+
|
|
713
871
|
/** An authored, unconditional program notice. */
|
|
714
872
|
export declare interface Notice {
|
|
715
873
|
readonly id: string;
|
package/dist/src/core/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { arrayOf, isArray, isFiniteNumber, isJSONValue, isRecord, isString, literalOf, recordOf, resolveField } from "@orkestrel/contract";
|
|
2
|
-
import { createQualifier, findRule, interpolateMessage, isQualificationDefinition, logicalPremises } from "@orkestrel/qualifier";
|
|
3
|
-
import { createRater, isRatingDefinition } from "@orkestrel/rater";
|
|
4
|
-
import { createEvaluator, createLogicalReasoner, createQuantitativeReasoner, createReason, findDuplicates, formatField, isFieldPath, isLogicalDefinition } from "@orkestrel/reason";
|
|
1
|
+
import { arrayOf, isArray, isBoolean, isFiniteNumber, isJSONValue, isNumber, isRecord, isString, literalOf, objectOf, recordOf, resolveField, whereOf } from "@orkestrel/contract";
|
|
2
|
+
import { createQualifier, findRule, interpolateMessage, isEligibility, isPremise, isQualificationDefinition, isQualificationResult, logicalPremises } from "@orkestrel/qualifier";
|
|
3
|
+
import { createRater, isRatingDefinition, isRatingResult } from "@orkestrel/rater";
|
|
4
|
+
import { createEvaluator, createLogicalReasoner, createQuantitativeReasoner, createReason, findDuplicates, formatField, isFieldPath, isLogicalDefinition, isLogicalResult } from "@orkestrel/reason";
|
|
5
5
|
import { Emitter } from "@orkestrel/emitter";
|
|
6
6
|
//#region src/core/constants.ts
|
|
7
7
|
/** Default definition validation policy for `createProgram` / `ProgramManager.add`. */
|
|
@@ -173,6 +173,202 @@ function isProgramDefinition(value) {
|
|
|
173
173
|
"metadata"
|
|
174
174
|
])(value);
|
|
175
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Determine whether a value is an open program sums record.
|
|
178
|
+
*
|
|
179
|
+
* @remarks
|
|
180
|
+
* Every own string-named property is checked, including non-enumerable
|
|
181
|
+
* properties. Inherited and symbol-named members are outside the record this
|
|
182
|
+
* guard certifies. Values remain plain JavaScript numbers, including `NaN` and
|
|
183
|
+
* infinities, because the published contract does not refine them.
|
|
184
|
+
*
|
|
185
|
+
* @param value - The candidate value
|
|
186
|
+
* @returns `true` when every own string-named value is a number
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```ts
|
|
190
|
+
* import { isProgramSums } from '@orkestrel/program'
|
|
191
|
+
*
|
|
192
|
+
* isProgramSums({ premium: 100 }) // true
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
function isProgramSums(value) {
|
|
196
|
+
return whereOf(objectOf({}), (record) => Object.getOwnPropertyNames(record).every((key) => isNumber(Reflect.get(record, key))))(value);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Determine whether a value is an open result-side {@link Determination}.
|
|
200
|
+
*
|
|
201
|
+
* @remarks
|
|
202
|
+
* Unknown members and class instances are admitted. Arrays are refused.
|
|
203
|
+
* Optional `scope` and `message` members may be absent or `undefined`.
|
|
204
|
+
*
|
|
205
|
+
* @param value - The candidate value
|
|
206
|
+
* @returns `true` when every published determination member conforms
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* import { isDetermination } from '@orkestrel/program'
|
|
211
|
+
*
|
|
212
|
+
* isDetermination({ id: 'audit', effect: 'notice', applied: true, premises: [] }) // true
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
var isDetermination = objectOf({
|
|
216
|
+
id: isString,
|
|
217
|
+
effect: isProgramEffect,
|
|
218
|
+
applied: isBoolean,
|
|
219
|
+
scope: isString,
|
|
220
|
+
message: isString,
|
|
221
|
+
premises: arrayOf(isPremise)
|
|
222
|
+
}, ["scope", "message"]);
|
|
223
|
+
/**
|
|
224
|
+
* Determine whether a value is an open result-side {@link AggregateGroup}.
|
|
225
|
+
*
|
|
226
|
+
* @remarks
|
|
227
|
+
* Unknown members and class instances are admitted. Arrays are refused.
|
|
228
|
+
*
|
|
229
|
+
* @param value - The candidate value
|
|
230
|
+
* @returns `true` when every published aggregate-group member conforms
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```ts
|
|
234
|
+
* import { isAggregateGroup } from '@orkestrel/program'
|
|
235
|
+
*
|
|
236
|
+
* isAggregateGroup({ key: 'east', count: 1, sums: { premium: 100 } }) // true
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
var isAggregateGroup = objectOf({
|
|
240
|
+
key: isString,
|
|
241
|
+
count: isNumber,
|
|
242
|
+
sums: isProgramSums
|
|
243
|
+
});
|
|
244
|
+
/**
|
|
245
|
+
* Determine whether a value is an open result-side {@link Tally}.
|
|
246
|
+
*
|
|
247
|
+
* @remarks
|
|
248
|
+
* Unknown members and class instances are admitted. Arrays are refused.
|
|
249
|
+
*
|
|
250
|
+
* @param value - The candidate value
|
|
251
|
+
* @returns `true` when every published tally member conforms
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* import { isTally } from '@orkestrel/program'
|
|
256
|
+
*
|
|
257
|
+
* isTally({ count: 1, sums: { premium: 100 } }) // true
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
var isTally = objectOf({
|
|
261
|
+
count: isNumber,
|
|
262
|
+
sums: isProgramSums
|
|
263
|
+
});
|
|
264
|
+
/**
|
|
265
|
+
* Determine whether a value is a total open status-tally record.
|
|
266
|
+
*
|
|
267
|
+
* @remarks
|
|
268
|
+
* Every {@link Status} in {@link STATUS_PRECEDENCE} is required and checked.
|
|
269
|
+
* Unknown members and class instances are admitted. Arrays are refused.
|
|
270
|
+
*
|
|
271
|
+
* @param value - The candidate value
|
|
272
|
+
* @returns `true` when every required status member is a {@link Tally}
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```ts
|
|
276
|
+
* import { emptyTallies, isTallies } from '@orkestrel/program'
|
|
277
|
+
*
|
|
278
|
+
* isTallies(emptyTallies([])) // true
|
|
279
|
+
* ```
|
|
280
|
+
*/
|
|
281
|
+
function isTallies(value) {
|
|
282
|
+
return whereOf(objectOf({}), (record) => STATUS_PRECEDENCE.every((status) => isTally(Reflect.get(record, status))))(value);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Determine whether a value is an open {@link ProgramResult}.
|
|
286
|
+
*
|
|
287
|
+
* @remarks
|
|
288
|
+
* This guard is result-postured for values returned through a borrowed
|
|
289
|
+
* {@link ProgramInterface}. It admits unknown members and class instances while
|
|
290
|
+
* composing qualifier's `isQualificationResult` and rater's `isRatingResult`
|
|
291
|
+
* over their complete nested result closures. Arrays are refused.
|
|
292
|
+
*
|
|
293
|
+
* @param value - The candidate value
|
|
294
|
+
* @returns `true` when every published program-result member conforms
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* ```ts
|
|
298
|
+
* import { isProgramResult } from '@orkestrel/program'
|
|
299
|
+
*
|
|
300
|
+
* isProgramResult(program.execute(subject)) // true
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
303
|
+
var isProgramResult = objectOf({
|
|
304
|
+
id: isString,
|
|
305
|
+
name: isString,
|
|
306
|
+
eligibility: isEligibility,
|
|
307
|
+
status: isStatus,
|
|
308
|
+
decision: isDecision,
|
|
309
|
+
qualification: isQualificationResult,
|
|
310
|
+
rating: isRatingResult,
|
|
311
|
+
determinations: arrayOf(isDetermination),
|
|
312
|
+
success: isBoolean,
|
|
313
|
+
trace: arrayOf(isString),
|
|
314
|
+
errors: arrayOf(isString)
|
|
315
|
+
}, ["decision", "rating"]);
|
|
316
|
+
/**
|
|
317
|
+
* Determine whether a value is an open {@link AggregateResult}.
|
|
318
|
+
*
|
|
319
|
+
* @remarks
|
|
320
|
+
* This guard is result-postured for values returned through a borrowed
|
|
321
|
+
* {@link ProgramInterface}. It admits unknown members and class instances while
|
|
322
|
+
* checking every nested program result, determination, group, total tally
|
|
323
|
+
* record, and sums record. Arrays are refused.
|
|
324
|
+
*
|
|
325
|
+
* @param value - The candidate value
|
|
326
|
+
* @returns `true` when every published aggregate-result member conforms
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* ```ts
|
|
330
|
+
* import { isAggregateResult } from '@orkestrel/program'
|
|
331
|
+
*
|
|
332
|
+
* isAggregateResult(program.execute(subjects)) // true
|
|
333
|
+
* ```
|
|
334
|
+
*/
|
|
335
|
+
var isAggregateResult = objectOf({
|
|
336
|
+
id: isString,
|
|
337
|
+
name: isString,
|
|
338
|
+
subjects: arrayOf(isProgramResult),
|
|
339
|
+
determinations: arrayOf(isDetermination),
|
|
340
|
+
groups: arrayOf(isAggregateGroup),
|
|
341
|
+
tallies: isTallies,
|
|
342
|
+
count: isNumber,
|
|
343
|
+
sums: isProgramSums,
|
|
344
|
+
success: isBoolean,
|
|
345
|
+
trace: arrayOf(isString),
|
|
346
|
+
errors: arrayOf(isString)
|
|
347
|
+
});
|
|
348
|
+
/**
|
|
349
|
+
* Determine whether a value is an open {@link ProgramValidationResult}.
|
|
350
|
+
*
|
|
351
|
+
* @remarks
|
|
352
|
+
* `ProgramValidationResult` is this package's own declared interface, not an
|
|
353
|
+
* alias of reason's validation result. This guard therefore checks the three
|
|
354
|
+
* program-owned members directly so the contracts may evolve independently.
|
|
355
|
+
* Unknown members and class instances are admitted. Arrays are refused.
|
|
356
|
+
*
|
|
357
|
+
* @param value - The candidate value
|
|
358
|
+
* @returns `true` when every published program-validation member conforms
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* ```ts
|
|
362
|
+
* import { isProgramValidationResult } from '@orkestrel/program'
|
|
363
|
+
*
|
|
364
|
+
* isProgramValidationResult({ valid: true, errors: [], warnings: [] }) // true
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
var isProgramValidationResult = objectOf({
|
|
368
|
+
valid: isBoolean,
|
|
369
|
+
errors: arrayOf(isString),
|
|
370
|
+
warnings: arrayOf(isString)
|
|
371
|
+
});
|
|
176
372
|
//#endregion
|
|
177
373
|
//#region src/core/helpers.ts
|
|
178
374
|
/**
|
|
@@ -977,6 +1173,83 @@ function buildAggregateResult(definition, subjects, determinations, groups, tall
|
|
|
977
1173
|
errors: [...subjects.flatMap((entry) => entry.errors), ...gateErrors]
|
|
978
1174
|
};
|
|
979
1175
|
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Build a {@link ProgramDefinition}.
|
|
1178
|
+
*
|
|
1179
|
+
* @remarks
|
|
1180
|
+
* Copies every collection and omits absent optional keys, so the returned
|
|
1181
|
+
* definition is a fresh, JSON-serializable value that never aliases its inputs.
|
|
1182
|
+
*
|
|
1183
|
+
* @param id - The program id
|
|
1184
|
+
* @param name - The display name
|
|
1185
|
+
* @param qualification - The nested qualification definition
|
|
1186
|
+
* @param rating - The nested rating definition; omit for an eligibility-only program
|
|
1187
|
+
* @param input - Optional description, notices, authority, aggregate, and metadata
|
|
1188
|
+
* @returns A fresh program definition
|
|
1189
|
+
*
|
|
1190
|
+
* @example
|
|
1191
|
+
* ```ts
|
|
1192
|
+
* import { programDefinition } from '@orkestrel/program'
|
|
1193
|
+
*
|
|
1194
|
+
* programDefinition('standard', 'Standard', qualification, rating, { notices: [notice] })
|
|
1195
|
+
* ```
|
|
1196
|
+
*/
|
|
1197
|
+
function programDefinition(id, name, qualification, rating, input) {
|
|
1198
|
+
return {
|
|
1199
|
+
id,
|
|
1200
|
+
name,
|
|
1201
|
+
qualification,
|
|
1202
|
+
...rating === void 0 ? {} : { rating },
|
|
1203
|
+
...input?.description === void 0 ? {} : { description: input.description },
|
|
1204
|
+
...input?.notices === void 0 ? {} : { notices: [...input.notices] },
|
|
1205
|
+
...input?.authority === void 0 ? {} : { authority: input.authority },
|
|
1206
|
+
...input?.aggregate === void 0 ? {} : { aggregate: input.aggregate },
|
|
1207
|
+
...input?.metadata === void 0 ? {} : { metadata: copyJSONValue(input.metadata) }
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Build a {@link Notice}.
|
|
1212
|
+
*
|
|
1213
|
+
* @param id - The notice id
|
|
1214
|
+
* @param message - The message template, carrying optional `{{token}}`s
|
|
1215
|
+
* @param input - Optional presentation scope
|
|
1216
|
+
* @returns A fresh notice
|
|
1217
|
+
*
|
|
1218
|
+
* @example
|
|
1219
|
+
* ```ts
|
|
1220
|
+
* import { noticeDefinition } from '@orkestrel/program'
|
|
1221
|
+
*
|
|
1222
|
+
* noticeDefinition('minimum', 'Minimum earned premium applies')
|
|
1223
|
+
* ```
|
|
1224
|
+
*/
|
|
1225
|
+
function noticeDefinition(id, message, input) {
|
|
1226
|
+
return {
|
|
1227
|
+
id,
|
|
1228
|
+
message,
|
|
1229
|
+
...input?.scope === void 0 ? {} : { scope: input.scope }
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Build an {@link AggregateDefinition}.
|
|
1234
|
+
*
|
|
1235
|
+
* @param fields - The aggregate fields to sum across a batch
|
|
1236
|
+
* @param input - Optional partition field and aggregate gates
|
|
1237
|
+
* @returns A fresh aggregate definition
|
|
1238
|
+
*
|
|
1239
|
+
* @example
|
|
1240
|
+
* ```ts
|
|
1241
|
+
* import { aggregateDefinition } from '@orkestrel/program'
|
|
1242
|
+
*
|
|
1243
|
+
* aggregateDefinition(['amount'], { by: 'location' })
|
|
1244
|
+
* ```
|
|
1245
|
+
*/
|
|
1246
|
+
function aggregateDefinition(fields, input) {
|
|
1247
|
+
return {
|
|
1248
|
+
fields: [...fields],
|
|
1249
|
+
...input?.by === void 0 ? {} : { by: input.by },
|
|
1250
|
+
...input?.gates === void 0 ? {} : { gates: input.gates }
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
980
1253
|
//#endregion
|
|
981
1254
|
//#region src/core/programs/Program.ts
|
|
982
1255
|
/**
|
|
@@ -1066,11 +1339,15 @@ var Program = class {
|
|
|
1066
1339
|
assertProgramSubject(subject);
|
|
1067
1340
|
const qualified = buildQualificationSubject(subject, aggregate);
|
|
1068
1341
|
const qualification = this.#qualifier.qualify(qualified, this.definition.qualification);
|
|
1342
|
+
if (!isQualificationResult(qualification)) throw new ProgramError("MISMATCH", "Qualifier returned invalid qualification result", this.definition.qualification.id);
|
|
1069
1343
|
this.#emitter.emit("qualify", qualification);
|
|
1070
1344
|
if (!qualification.success || qualification.eligibility !== "eligible") return this.#finish(subject, qualification, void 0);
|
|
1071
1345
|
const lines = selectProgramLines(this.definition.rating?.lines ?? [], qualification.scopes);
|
|
1072
1346
|
const rating = lines.length === 0 ? void 0 : this.#rater.rate(lines, subject);
|
|
1073
|
-
if (rating !== void 0)
|
|
1347
|
+
if (rating !== void 0) {
|
|
1348
|
+
if (!isRatingResult(rating)) throw new ProgramError("MISMATCH", "Rater returned invalid rating result", this.definition.rating?.id);
|
|
1349
|
+
this.#emitter.emit("rate", rating);
|
|
1350
|
+
}
|
|
1074
1351
|
return this.#finish(subject, qualification, rating);
|
|
1075
1352
|
}
|
|
1076
1353
|
#finish(subject, qualification, rating) {
|
|
@@ -1085,7 +1362,7 @@ var Program = class {
|
|
|
1085
1362
|
}
|
|
1086
1363
|
const outcome = { [OUTCOME_KEY]: buildOutcomeProjection(result) };
|
|
1087
1364
|
const resolved = this.#engine.reason(outcome, authority);
|
|
1088
|
-
if (resolved
|
|
1365
|
+
if (!isLogicalResult(resolved)) throw new ProgramError("MISMATCH", "Authority returned invalid logical result", authority.id);
|
|
1089
1366
|
const limits = buildLimits(authority, resolved, outcome, this.#evaluator, this.#labels);
|
|
1090
1367
|
for (const limit of limits) this.#emitter.emit("determine", limit);
|
|
1091
1368
|
result = buildProgramResult(this.definition, qualification, rating, [...notices, ...limits], status, { authority: resolved });
|
|
@@ -1116,7 +1393,7 @@ var Program = class {
|
|
|
1116
1393
|
if (gates === void 0) return { determinations: [] };
|
|
1117
1394
|
const record = buildAggregateRecord(count, sums, groups);
|
|
1118
1395
|
const resolved = this.#engine.reason(record, gates);
|
|
1119
|
-
if (resolved
|
|
1396
|
+
if (!isLogicalResult(resolved)) throw new ProgramError("MISMATCH", "Aggregate gates returned invalid logical result", gates.id);
|
|
1120
1397
|
const determinations = buildLimits(gates, resolved, record, this.#evaluator, this.#labels);
|
|
1121
1398
|
for (const determination of determinations) this.#emitter.emit("determine", determination);
|
|
1122
1399
|
return {
|
|
@@ -1307,84 +1584,7 @@ function createProgram(definition, options) {
|
|
|
1307
1584
|
function createProgramManager(options) {
|
|
1308
1585
|
return new ProgramManager(options);
|
|
1309
1586
|
}
|
|
1310
|
-
/**
|
|
1311
|
-
* Build a {@link ProgramDefinition}.
|
|
1312
|
-
*
|
|
1313
|
-
* @remarks
|
|
1314
|
-
* Copies every collection and omits absent optional keys, so the returned
|
|
1315
|
-
* definition is a fresh, JSON-serializable value that never aliases its inputs.
|
|
1316
|
-
*
|
|
1317
|
-
* @param id - The program id
|
|
1318
|
-
* @param name - The display name
|
|
1319
|
-
* @param qualification - The nested qualification definition
|
|
1320
|
-
* @param rating - The nested rating definition; omit for an eligibility-only program
|
|
1321
|
-
* @param input - Optional description, notices, authority, aggregate, and metadata
|
|
1322
|
-
* @returns A fresh program definition
|
|
1323
|
-
*
|
|
1324
|
-
* @example
|
|
1325
|
-
* ```ts
|
|
1326
|
-
* import { programDefinition } from '@orkestrel/program'
|
|
1327
|
-
*
|
|
1328
|
-
* programDefinition('standard', 'Standard', qualification, rating, { notices: [notice] })
|
|
1329
|
-
* ```
|
|
1330
|
-
*/
|
|
1331
|
-
function programDefinition(id, name, qualification, rating, input) {
|
|
1332
|
-
return {
|
|
1333
|
-
id,
|
|
1334
|
-
name,
|
|
1335
|
-
qualification,
|
|
1336
|
-
...rating === void 0 ? {} : { rating },
|
|
1337
|
-
...input?.description === void 0 ? {} : { description: input.description },
|
|
1338
|
-
...input?.notices === void 0 ? {} : { notices: [...input.notices] },
|
|
1339
|
-
...input?.authority === void 0 ? {} : { authority: input.authority },
|
|
1340
|
-
...input?.aggregate === void 0 ? {} : { aggregate: input.aggregate },
|
|
1341
|
-
...input?.metadata === void 0 ? {} : { metadata: copyJSONValue(input.metadata) }
|
|
1342
|
-
};
|
|
1343
|
-
}
|
|
1344
|
-
/**
|
|
1345
|
-
* Build a {@link Notice}.
|
|
1346
|
-
*
|
|
1347
|
-
* @param id - The notice id
|
|
1348
|
-
* @param message - The message template, carrying optional `{{token}}`s
|
|
1349
|
-
* @param input - Optional presentation scope
|
|
1350
|
-
* @returns A fresh notice
|
|
1351
|
-
*
|
|
1352
|
-
* @example
|
|
1353
|
-
* ```ts
|
|
1354
|
-
* import { noticeDefinition } from '@orkestrel/program'
|
|
1355
|
-
*
|
|
1356
|
-
* noticeDefinition('minimum', 'Minimum earned premium applies')
|
|
1357
|
-
* ```
|
|
1358
|
-
*/
|
|
1359
|
-
function noticeDefinition(id, message, input) {
|
|
1360
|
-
return {
|
|
1361
|
-
id,
|
|
1362
|
-
message,
|
|
1363
|
-
...input?.scope === void 0 ? {} : { scope: input.scope }
|
|
1364
|
-
};
|
|
1365
|
-
}
|
|
1366
|
-
/**
|
|
1367
|
-
* Build an {@link AggregateDefinition}.
|
|
1368
|
-
*
|
|
1369
|
-
* @param fields - The aggregate fields to sum across a batch
|
|
1370
|
-
* @param input - Optional partition field and aggregate gates
|
|
1371
|
-
* @returns A fresh aggregate definition
|
|
1372
|
-
*
|
|
1373
|
-
* @example
|
|
1374
|
-
* ```ts
|
|
1375
|
-
* import { aggregateDefinition } from '@orkestrel/program'
|
|
1376
|
-
*
|
|
1377
|
-
* aggregateDefinition(['amount'], { by: 'location' })
|
|
1378
|
-
* ```
|
|
1379
|
-
*/
|
|
1380
|
-
function aggregateDefinition(fields, input) {
|
|
1381
|
-
return {
|
|
1382
|
-
fields: [...fields],
|
|
1383
|
-
...input?.by === void 0 ? {} : { by: input.by },
|
|
1384
|
-
...input?.gates === void 0 ? {} : { gates: input.gates }
|
|
1385
|
-
};
|
|
1386
|
-
}
|
|
1387
1587
|
//#endregion
|
|
1388
|
-
export { AGGREGATE_KEY, DEFAULT_PROGRAM_VALIDATE, ELIGIBILITY_DECISIONS, OUTCOME_KEY, Program, ProgramError, ProgramManager, STATUS_PRECEDENCE, aggregateDefinition, aggregateGroups, aggregateSums, assertProgramDefinition, assertProgramSubject, buildAggregateProjection, buildAggregateRecord, buildAggregateResult, buildLimits, buildNotices, buildOutcomeProjection, buildProgramResult, buildQualificationSubject, completeTallies, copyJSONValue, createProgram, createProgramManager, decideEligibility, deriveStatus, emptySums, emptyTallies, findMissingScopes, formatGroupKey, hasReservedKey, isAggregateDefinition, isDecision, isNotice, isProgramDefinition, isProgramEffect, isProgramError, isStatus, noticeDefinition, programDefinition, selectProgramLines, sumFields, tallyProgram, validateProgramDefinition };
|
|
1588
|
+
export { AGGREGATE_KEY, DEFAULT_PROGRAM_VALIDATE, ELIGIBILITY_DECISIONS, OUTCOME_KEY, Program, ProgramError, ProgramManager, STATUS_PRECEDENCE, aggregateDefinition, aggregateGroups, aggregateSums, assertProgramDefinition, assertProgramSubject, buildAggregateProjection, buildAggregateRecord, buildAggregateResult, buildLimits, buildNotices, buildOutcomeProjection, buildProgramResult, buildQualificationSubject, completeTallies, copyJSONValue, createProgram, createProgramManager, decideEligibility, deriveStatus, emptySums, emptyTallies, findMissingScopes, formatGroupKey, hasReservedKey, isAggregateDefinition, isAggregateGroup, isAggregateResult, isDecision, isDetermination, isNotice, isProgramDefinition, isProgramEffect, isProgramError, isProgramResult, isProgramSums, isProgramValidationResult, isStatus, isTallies, isTally, noticeDefinition, programDefinition, selectProgramLines, sumFields, tallyProgram, validateProgramDefinition };
|
|
1389
1589
|
|
|
1390
1590
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orkestrel/program",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "A program composition layer that orchestrates @orkestrel/qualifier and @orkestrel/rater into one execute workflow with notices, authority, status, decisions, and batch aggregation. Part of the @orkestrel line.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aggregate",
|
|
@@ -60,10 +60,11 @@
|
|
|
60
60
|
"format": "oxfmt --config .oxfmtrc.json --write .",
|
|
61
61
|
"format:check": "oxfmt --config .oxfmtrc.json --check .",
|
|
62
62
|
"lint:check": "oxlint --config .oxlintrc.json --deny-warnings .",
|
|
63
|
-
"test": "npm run test:src && npm run test:policy && npm run test:guides",
|
|
63
|
+
"test": "npm run test:src && npm run test:policy && npm run test:config && npm run test:guides",
|
|
64
64
|
"test:src": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:core",
|
|
65
65
|
"test:src:core": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:core",
|
|
66
66
|
"test:policy": "vitest run --config vite.config.ts --no-cache --reporter=dot --project policy",
|
|
67
|
+
"test:config": "vitest run --config vite.config.ts --no-cache --reporter=dot --project config",
|
|
67
68
|
"test:guides": "vitest run --config vite.config.ts --reporter=dot --project guides",
|
|
68
69
|
"build": "npm run clean && npm run build:src",
|
|
69
70
|
"build:src": "npm run build:src:core",
|
|
@@ -71,22 +72,23 @@
|
|
|
71
72
|
"prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test"
|
|
72
73
|
},
|
|
73
74
|
"dependencies": {
|
|
74
|
-
"@orkestrel/contract": "^0.0.
|
|
75
|
-
"@orkestrel/emitter": "^0.0.
|
|
76
|
-
"@orkestrel/qualifier": "^0.0.
|
|
77
|
-
"@orkestrel/rater": "^0.0.
|
|
78
|
-
"@orkestrel/reason": "^0.0.
|
|
75
|
+
"@orkestrel/contract": "^0.0.12",
|
|
76
|
+
"@orkestrel/emitter": "^0.0.7",
|
|
77
|
+
"@orkestrel/qualifier": "^0.0.10",
|
|
78
|
+
"@orkestrel/rater": "^0.0.11",
|
|
79
|
+
"@orkestrel/reason": "^0.0.7"
|
|
79
80
|
},
|
|
80
81
|
"devDependencies": {
|
|
81
82
|
"@microsoft/api-extractor": "^7.58.12",
|
|
82
|
-
"@orkestrel/guide": "^0.0.
|
|
83
|
-
"@orkestrel/scaffold": "^0.0.
|
|
84
|
-
"@
|
|
83
|
+
"@orkestrel/guide": "^0.0.12",
|
|
84
|
+
"@orkestrel/scaffold": "^0.0.39",
|
|
85
|
+
"@orkestrel/test": "^0.0.6",
|
|
86
|
+
"@types/node": "^26.2.0",
|
|
85
87
|
"@vitest/browser-playwright": "^4.1.10",
|
|
86
|
-
"oxfmt": "^0.
|
|
87
|
-
"oxlint": "^1.
|
|
88
|
+
"oxfmt": "^0.63.0",
|
|
89
|
+
"oxlint": "^1.78.0",
|
|
88
90
|
"typescript": "^6.0.3",
|
|
89
|
-
"vite": "^8.1
|
|
91
|
+
"vite": "^8.2.1",
|
|
90
92
|
"vite-plugin-dts": "^5.0.3",
|
|
91
93
|
"vitest": "^4.1.10"
|
|
92
94
|
},
|