@orkestrel/program 0.0.10 → 0.0.12
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/README.md +25 -21
- package/dist/src/core/index.cjs +406 -204
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +1800 -1180
- package/dist/src/core/index.d.ts +1800 -1180
- package/dist/src/core/index.js +399 -196
- package/dist/src/core/index.js.map +1 -1
- package/package.json +21 -18
package/dist/src/core/index.js
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
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,
|
|
2
|
+
import { createQualifier, findRule, interpolateMessage, isEligibility, isPremise, isQualificationDefinition, isQualificationResult, ruleToPremises } from "@orkestrel/qualifier";
|
|
3
3
|
import { createRater, isRatingDefinition, isRatingResult } from "@orkestrel/rater";
|
|
4
4
|
import { createEvaluator, createLogicalReasoner, createQuantitativeReasoner, createReason, findDuplicates, formatField, isFieldPath, isLogicalDefinition, isLogicalResult, isReasonValidationResult } from "@orkestrel/reason";
|
|
5
5
|
import { Emitter } from "@orkestrel/emitter";
|
|
6
6
|
//#region src/core/constants.ts
|
|
7
|
-
/**
|
|
7
|
+
/** Names the default definition validation policy for `createProgram` / `ProgramManager.add`. */
|
|
8
8
|
var DEFAULT_PROGRAM_VALIDATE = true;
|
|
9
|
-
/**
|
|
10
|
-
var
|
|
9
|
+
/** Lists every {@link Status} literal — the source the union and its guard derive from. */
|
|
10
|
+
var STATUSES = Object.freeze([
|
|
11
11
|
"ineligible",
|
|
12
12
|
"referral",
|
|
13
13
|
"conditional",
|
|
14
14
|
"unrated",
|
|
15
15
|
"eligible"
|
|
16
16
|
]);
|
|
17
|
-
/**
|
|
17
|
+
/** Maps each global eligibility to its deterministic authority decision. */
|
|
18
18
|
var ELIGIBILITY_DECISIONS = Object.freeze({
|
|
19
19
|
eligible: "approved",
|
|
20
20
|
ineligible: "denied",
|
|
21
21
|
referral: "submitted"
|
|
22
22
|
});
|
|
23
|
-
/**
|
|
23
|
+
/** Names the reserved working-subject key a batch's aggregate projection is written under. */
|
|
24
24
|
var AGGREGATE_KEY = "aggregate";
|
|
25
|
-
/**
|
|
25
|
+
/** Names the reserved working-subject key the authority's outcome projection is written under. */
|
|
26
26
|
var OUTCOME_KEY = "outcome";
|
|
27
27
|
//#endregion
|
|
28
28
|
//#region src/core/errors.ts
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* Reports a coded programmer error thrown by the program layer.
|
|
31
31
|
*
|
|
32
32
|
* @remarks
|
|
33
33
|
* `DUPLICATE` — a program id collision on `ProgramManager.add`, or a duplicate
|
|
@@ -37,28 +37,57 @@ var OUTCOME_KEY = "outcome";
|
|
|
37
37
|
* policy failed validation. `MISMATCH` — an injected entity or a returned
|
|
38
38
|
* reason result has the wrong contract. `RESERVED` — a subject already
|
|
39
39
|
* carries `aggregate` or `outcome`. `DESTROYED` — use of a destroyed entity.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* import { ProgramError } from '@orkestrel/program'
|
|
44
|
+
*
|
|
45
|
+
* const error = new ProgramError('RESERVED', 'Subject carries a reserved key', 'aggregate')
|
|
46
|
+
* error.code // 'RESERVED'
|
|
47
|
+
* ```
|
|
40
48
|
*/
|
|
41
49
|
var ProgramError = class extends Error {
|
|
42
50
|
code;
|
|
43
51
|
context;
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Creates a coded program error.
|
|
54
|
+
*
|
|
55
|
+
* @param code - The machine-readable failure category
|
|
56
|
+
* @param message - The human-readable failure description
|
|
57
|
+
* @param context - Optional structured context for the failure
|
|
58
|
+
* @param cause - Optional underlying value the failure wraps
|
|
59
|
+
*/
|
|
60
|
+
constructor(code, message, context, cause) {
|
|
61
|
+
super(message, cause === void 0 ? void 0 : { cause });
|
|
46
62
|
this.name = "ProgramError";
|
|
47
63
|
this.code = code;
|
|
48
64
|
this.context = context;
|
|
49
65
|
}
|
|
50
66
|
};
|
|
51
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Determines whether a caught value is a {@link ProgramError}.
|
|
69
|
+
*
|
|
70
|
+
* @param value - The candidate value
|
|
71
|
+
* @returns True if the value is a {@link ProgramError}; false otherwise
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* import { isProgramError, ProgramError } from '@orkestrel/program'
|
|
76
|
+
*
|
|
77
|
+
* isProgramError(new ProgramError('RESERVED', 'Subject carries a reserved key')) // true
|
|
78
|
+
* isProgramError(new Error('Subject carries a reserved key')) // false
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
52
81
|
function isProgramError(value) {
|
|
53
82
|
return value instanceof ProgramError;
|
|
54
83
|
}
|
|
55
84
|
//#endregion
|
|
56
85
|
//#region src/core/validators.ts
|
|
57
86
|
/**
|
|
58
|
-
*
|
|
87
|
+
* Determines whether a value is a {@link Decision} literal.
|
|
59
88
|
*
|
|
60
89
|
* @param value - The candidate value
|
|
61
|
-
* @returns
|
|
90
|
+
* @returns True if `value` is a {@link Decision}; false otherwise
|
|
62
91
|
*
|
|
63
92
|
* @example
|
|
64
93
|
* ```ts
|
|
@@ -69,10 +98,10 @@ function isProgramError(value) {
|
|
|
69
98
|
*/
|
|
70
99
|
var isDecision = literalOf("approved", "denied", "submitted");
|
|
71
100
|
/**
|
|
72
|
-
*
|
|
101
|
+
* Determines whether a value is a {@link Status} literal.
|
|
73
102
|
*
|
|
74
103
|
* @param value - The candidate value
|
|
75
|
-
* @returns
|
|
104
|
+
* @returns True if `value` is a {@link Status}; false otherwise
|
|
76
105
|
*
|
|
77
106
|
* @example
|
|
78
107
|
* ```ts
|
|
@@ -81,12 +110,12 @@ var isDecision = literalOf("approved", "denied", "submitted");
|
|
|
81
110
|
* isStatus('eligible') // true
|
|
82
111
|
* ```
|
|
83
112
|
*/
|
|
84
|
-
var isStatus = literalOf(
|
|
113
|
+
var isStatus = literalOf(STATUSES);
|
|
85
114
|
/**
|
|
86
|
-
*
|
|
115
|
+
* Determines whether a value is a {@link ProgramEffect} literal.
|
|
87
116
|
*
|
|
88
117
|
* @param value - The candidate value
|
|
89
|
-
* @returns
|
|
118
|
+
* @returns True if `value` is a {@link ProgramEffect}; false otherwise
|
|
90
119
|
*
|
|
91
120
|
* @example
|
|
92
121
|
* ```ts
|
|
@@ -97,10 +126,10 @@ var isStatus = literalOf("ineligible", "referral", "conditional", "unrated", "el
|
|
|
97
126
|
*/
|
|
98
127
|
var isProgramEffect = literalOf("notice", "limit");
|
|
99
128
|
/**
|
|
100
|
-
*
|
|
129
|
+
* Determines whether a value is an exact {@link Notice} record.
|
|
101
130
|
*
|
|
102
131
|
* @param value - The candidate value
|
|
103
|
-
* @returns
|
|
132
|
+
* @returns True if `value` is a {@link Notice}; false otherwise
|
|
104
133
|
*
|
|
105
134
|
* @example
|
|
106
135
|
* ```ts
|
|
@@ -117,10 +146,10 @@ function isNotice(value) {
|
|
|
117
146
|
}, ["scope"])(value);
|
|
118
147
|
}
|
|
119
148
|
/**
|
|
120
|
-
*
|
|
149
|
+
* Determines whether a value is an exact {@link AggregateDefinition} record.
|
|
121
150
|
*
|
|
122
151
|
* @param value - The candidate value
|
|
123
|
-
* @returns
|
|
152
|
+
* @returns True if `value` is an {@link AggregateDefinition}; false otherwise
|
|
124
153
|
*
|
|
125
154
|
* @example
|
|
126
155
|
* ```ts
|
|
@@ -132,19 +161,19 @@ function isNotice(value) {
|
|
|
132
161
|
function isAggregateDefinition(value) {
|
|
133
162
|
return recordOf({
|
|
134
163
|
fields: arrayOf(isFieldPath),
|
|
135
|
-
|
|
164
|
+
partition: isFieldPath,
|
|
136
165
|
gates: isLogicalDefinition
|
|
137
|
-
}, ["
|
|
166
|
+
}, ["partition", "gates"])(value);
|
|
138
167
|
}
|
|
139
168
|
/**
|
|
140
|
-
*
|
|
169
|
+
* Determines whether a value is an exact {@link ProgramDefinition} record.
|
|
141
170
|
*
|
|
142
171
|
* @remarks
|
|
143
172
|
* `rating` is optional — an omitted `rating` authors an eligibility-only
|
|
144
173
|
* program (see {@link ProgramDefinition}).
|
|
145
174
|
*
|
|
146
175
|
* @param value - The candidate value
|
|
147
|
-
* @returns
|
|
176
|
+
* @returns True if `value` is a {@link ProgramDefinition}; false otherwise
|
|
148
177
|
*
|
|
149
178
|
* @example
|
|
150
179
|
* ```ts
|
|
@@ -174,7 +203,7 @@ function isProgramDefinition(value) {
|
|
|
174
203
|
])(value);
|
|
175
204
|
}
|
|
176
205
|
/**
|
|
177
|
-
*
|
|
206
|
+
* Determines whether a value is an open program sums record.
|
|
178
207
|
*
|
|
179
208
|
* @remarks
|
|
180
209
|
* Every own string-named property is checked, including non-enumerable
|
|
@@ -183,7 +212,7 @@ function isProgramDefinition(value) {
|
|
|
183
212
|
* infinities, because the published contract does not refine them.
|
|
184
213
|
*
|
|
185
214
|
* @param value - The candidate value
|
|
186
|
-
* @returns
|
|
215
|
+
* @returns True if every own string-named value is a number; false otherwise
|
|
187
216
|
*
|
|
188
217
|
* @example
|
|
189
218
|
* ```ts
|
|
@@ -196,14 +225,14 @@ function isProgramSums(value) {
|
|
|
196
225
|
return whereOf(objectOf({}), (record) => Object.getOwnPropertyNames(record).every((key) => isNumber(Reflect.get(record, key))))(value);
|
|
197
226
|
}
|
|
198
227
|
/**
|
|
199
|
-
*
|
|
228
|
+
* Determines whether a value is an open result-side {@link Determination}.
|
|
200
229
|
*
|
|
201
230
|
* @remarks
|
|
202
231
|
* Unknown members and class instances are admitted. Arrays are refused.
|
|
203
232
|
* Optional `scope` and `message` members may be absent or `undefined`.
|
|
204
233
|
*
|
|
205
234
|
* @param value - The candidate value
|
|
206
|
-
* @returns
|
|
235
|
+
* @returns True if every published determination member conforms; false otherwise
|
|
207
236
|
*
|
|
208
237
|
* @example
|
|
209
238
|
* ```ts
|
|
@@ -221,13 +250,13 @@ var isDetermination = objectOf({
|
|
|
221
250
|
premises: arrayOf(isPremise)
|
|
222
251
|
}, ["scope", "message"]);
|
|
223
252
|
/**
|
|
224
|
-
*
|
|
253
|
+
* Determines whether a value is an open result-side {@link AggregateGroup}.
|
|
225
254
|
*
|
|
226
255
|
* @remarks
|
|
227
256
|
* Unknown members and class instances are admitted. Arrays are refused.
|
|
228
257
|
*
|
|
229
258
|
* @param value - The candidate value
|
|
230
|
-
* @returns
|
|
259
|
+
* @returns True if every published aggregate-group member conforms; false otherwise
|
|
231
260
|
*
|
|
232
261
|
* @example
|
|
233
262
|
* ```ts
|
|
@@ -242,13 +271,13 @@ var isAggregateGroup = objectOf({
|
|
|
242
271
|
sums: isProgramSums
|
|
243
272
|
});
|
|
244
273
|
/**
|
|
245
|
-
*
|
|
274
|
+
* Determines whether a value is an open result-side {@link Tally}.
|
|
246
275
|
*
|
|
247
276
|
* @remarks
|
|
248
277
|
* Unknown members and class instances are admitted. Arrays are refused.
|
|
249
278
|
*
|
|
250
279
|
* @param value - The candidate value
|
|
251
|
-
* @returns
|
|
280
|
+
* @returns True if every published tally member conforms; false otherwise
|
|
252
281
|
*
|
|
253
282
|
* @example
|
|
254
283
|
* ```ts
|
|
@@ -262,27 +291,27 @@ var isTally = objectOf({
|
|
|
262
291
|
sums: isProgramSums
|
|
263
292
|
});
|
|
264
293
|
/**
|
|
265
|
-
*
|
|
294
|
+
* Determines whether a value is a total open status-tally record.
|
|
266
295
|
*
|
|
267
296
|
* @remarks
|
|
268
|
-
* Every {@link Status} in {@link
|
|
297
|
+
* Every {@link Status} in {@link STATUSES} is required and checked.
|
|
269
298
|
* Unknown members and class instances are admitted. Arrays are refused.
|
|
270
299
|
*
|
|
271
300
|
* @param value - The candidate value
|
|
272
|
-
* @returns
|
|
301
|
+
* @returns True if every required status member is a {@link Tally}; false otherwise
|
|
273
302
|
*
|
|
274
303
|
* @example
|
|
275
304
|
* ```ts
|
|
276
|
-
* import {
|
|
305
|
+
* import { buildEmptyTallies, isTallies } from '@orkestrel/program'
|
|
277
306
|
*
|
|
278
|
-
* isTallies(
|
|
307
|
+
* isTallies(buildEmptyTallies([])) // true
|
|
279
308
|
* ```
|
|
280
309
|
*/
|
|
281
310
|
function isTallies(value) {
|
|
282
|
-
return whereOf(objectOf({}), (record) =>
|
|
311
|
+
return whereOf(objectOf({}), (record) => STATUSES.every((status) => isTally(Reflect.get(record, status))))(value);
|
|
283
312
|
}
|
|
284
313
|
/**
|
|
285
|
-
*
|
|
314
|
+
* Determines whether a value is an open {@link ProgramResult}.
|
|
286
315
|
*
|
|
287
316
|
* @remarks
|
|
288
317
|
* This guard is result-postured for values returned through a borrowed
|
|
@@ -291,7 +320,7 @@ function isTallies(value) {
|
|
|
291
320
|
* over their complete nested result closures. Arrays are refused.
|
|
292
321
|
*
|
|
293
322
|
* @param value - The candidate value
|
|
294
|
-
* @returns
|
|
323
|
+
* @returns True if every published program-result member conforms; false otherwise
|
|
295
324
|
*
|
|
296
325
|
* @example
|
|
297
326
|
* ```ts
|
|
@@ -314,7 +343,7 @@ var isProgramResult = objectOf({
|
|
|
314
343
|
errors: arrayOf(isString)
|
|
315
344
|
}, ["decision", "rating"]);
|
|
316
345
|
/**
|
|
317
|
-
*
|
|
346
|
+
* Determines whether a value is an open {@link AggregateResult}.
|
|
318
347
|
*
|
|
319
348
|
* @remarks
|
|
320
349
|
* This guard is result-postured for values returned through a borrowed
|
|
@@ -323,7 +352,7 @@ var isProgramResult = objectOf({
|
|
|
323
352
|
* record, and sums record. Arrays are refused.
|
|
324
353
|
*
|
|
325
354
|
* @param value - The candidate value
|
|
326
|
-
* @returns
|
|
355
|
+
* @returns True if every published aggregate-result member conforms; false otherwise
|
|
327
356
|
*
|
|
328
357
|
* @example
|
|
329
358
|
* ```ts
|
|
@@ -346,16 +375,16 @@ var isAggregateResult = objectOf({
|
|
|
346
375
|
errors: arrayOf(isString)
|
|
347
376
|
});
|
|
348
377
|
/**
|
|
349
|
-
*
|
|
378
|
+
* Determines whether a value is an open {@link ProgramValidationResult}.
|
|
350
379
|
*
|
|
351
380
|
* @remarks
|
|
352
381
|
* `ProgramValidationResult` is this package's own declared interface, not an
|
|
353
|
-
* alias of reason's validation result. This guard therefore checks the
|
|
382
|
+
* alias of reason's validation result. This guard therefore checks the
|
|
354
383
|
* program-owned members directly so the contracts may evolve independently.
|
|
355
384
|
* Unknown members and class instances are admitted. Arrays are refused.
|
|
356
385
|
*
|
|
357
386
|
* @param value - The candidate value
|
|
358
|
-
* @returns
|
|
387
|
+
* @returns True if every published program-validation member conforms; false otherwise
|
|
359
388
|
*
|
|
360
389
|
* @example
|
|
361
390
|
* ```ts
|
|
@@ -372,38 +401,7 @@ var isProgramValidationResult = objectOf({
|
|
|
372
401
|
//#endregion
|
|
373
402
|
//#region src/core/helpers.ts
|
|
374
403
|
/**
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* @remarks
|
|
378
|
-
* The input must be an acyclic JSON tree of bounded depth — a pathologically
|
|
379
|
-
* deep tree throws the engine's `RangeError` (stack exhaustion) rather than
|
|
380
|
-
* hanging. Each copied record uses `Object.defineProperty` for own-property
|
|
381
|
-
* definition, which defends against prototype-pollution keys (`__proto__`).
|
|
382
|
-
*
|
|
383
|
-
* @param value - The JSON value to copy
|
|
384
|
-
* @returns A fresh JSON value
|
|
385
|
-
*
|
|
386
|
-
* @example
|
|
387
|
-
* ```ts
|
|
388
|
-
* import { copyJSONValue } from '@orkestrel/program'
|
|
389
|
-
*
|
|
390
|
-
* copyJSONValue({ a: [1, 2] }) // { a: [1, 2] }, a fresh copy
|
|
391
|
-
* ```
|
|
392
|
-
*/
|
|
393
|
-
function copyJSONValue(value) {
|
|
394
|
-
if (value === null || typeof value !== "object") return value;
|
|
395
|
-
if (Array.isArray(value)) return value.map(copyJSONValue);
|
|
396
|
-
const copy = {};
|
|
397
|
-
for (const [key, entry] of Object.entries(value)) Object.defineProperty(copy, key, {
|
|
398
|
-
value: copyJSONValue(entry),
|
|
399
|
-
enumerable: true,
|
|
400
|
-
writable: true,
|
|
401
|
-
configurable: true
|
|
402
|
-
});
|
|
403
|
-
return copy;
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* Determine whether a caller subject already carries a reserved program key.
|
|
404
|
+
* Determines whether a caller subject already carries a reserved program key.
|
|
407
405
|
*
|
|
408
406
|
* @remarks
|
|
409
407
|
* `aggregate` and `outcome` are program-private working-subject namespaces — the
|
|
@@ -412,7 +410,7 @@ function copyJSONValue(value) {
|
|
|
412
410
|
* collide with a projection, so it is rejected before qualification.
|
|
413
411
|
*
|
|
414
412
|
* @param subject - The caller subject to check
|
|
415
|
-
* @returns
|
|
413
|
+
* @returns True if the subject owns `aggregate` or `outcome`; false otherwise
|
|
416
414
|
*
|
|
417
415
|
* @example
|
|
418
416
|
* ```ts
|
|
@@ -426,11 +424,12 @@ function hasReservedKey(subject) {
|
|
|
426
424
|
return Object.hasOwn(subject, "aggregate") || Object.hasOwn(subject, "outcome");
|
|
427
425
|
}
|
|
428
426
|
/**
|
|
429
|
-
*
|
|
427
|
+
* Asserts a value is a valid program {@link Subject}, narrowing it in place.
|
|
430
428
|
*
|
|
431
429
|
* @param subject - The candidate subject to validate
|
|
432
|
-
* @throws {@link ProgramError}
|
|
433
|
-
*
|
|
430
|
+
* @throws {@link ProgramError} Thrown when the value is not a record (`'MISMATCH'`).
|
|
431
|
+
* @throws {@link ProgramError} Thrown when the value already carries the `aggregate`
|
|
432
|
+
* or `outcome` key (`'RESERVED'`).
|
|
434
433
|
*
|
|
435
434
|
* @example
|
|
436
435
|
* ```ts
|
|
@@ -447,7 +446,7 @@ function assertProgramSubject(subject) {
|
|
|
447
446
|
}
|
|
448
447
|
}
|
|
449
448
|
/**
|
|
450
|
-
*
|
|
449
|
+
* Selects the rating lines a subject may be rated on from scoped eligibility.
|
|
451
450
|
*
|
|
452
451
|
* @remarks
|
|
453
452
|
* A scope names a rating-line id. A line survives when its scope is absent
|
|
@@ -474,11 +473,11 @@ function selectProgramLines(lines, scopes) {
|
|
|
474
473
|
});
|
|
475
474
|
}
|
|
476
475
|
/**
|
|
477
|
-
*
|
|
476
|
+
* Derives the final program {@link Status} from a definition's rating policy and
|
|
478
477
|
* qualification/rating evidence.
|
|
479
478
|
*
|
|
480
479
|
* @remarks
|
|
481
|
-
* Explicit policy, not an opaque precedence reduce
|
|
480
|
+
* Explicit policy, not an opaque precedence reduce: global
|
|
482
481
|
* ineligibility or referral is terminal; a scoped referral yields `referral`;
|
|
483
482
|
* an applied `condition` or an applied scoped `restriction` (a line was
|
|
484
483
|
* removed but others rated) is `conditional`. When the definition OMITS
|
|
@@ -508,7 +507,7 @@ function deriveStatus(definition, qualification, rating) {
|
|
|
508
507
|
return conditional ? "conditional" : "eligible";
|
|
509
508
|
}
|
|
510
509
|
/**
|
|
511
|
-
*
|
|
510
|
+
* Maps a global {@link Eligibility} to its deterministic authority {@link Decision}.
|
|
512
511
|
*
|
|
513
512
|
* @param eligibility - The global eligibility
|
|
514
513
|
* @returns The matching decision
|
|
@@ -525,8 +524,8 @@ function decideEligibility(eligibility) {
|
|
|
525
524
|
return ELIGIBILITY_DECISIONS[eligibility];
|
|
526
525
|
}
|
|
527
526
|
/**
|
|
528
|
-
*
|
|
529
|
-
* {@link Determination}
|
|
527
|
+
* Resolves authored {@link Notice} values into unconditionally-applied `notice`
|
|
528
|
+
* {@link Determination} values.
|
|
530
529
|
*
|
|
531
530
|
* @remarks
|
|
532
531
|
* Notices are program output only — they never affect eligibility, status, line
|
|
@@ -539,12 +538,12 @@ function decideEligibility(eligibility) {
|
|
|
539
538
|
*
|
|
540
539
|
* @example
|
|
541
540
|
* ```ts
|
|
542
|
-
* import {
|
|
541
|
+
* import { buildNoticeDeterminations } from '@orkestrel/program'
|
|
543
542
|
*
|
|
544
|
-
*
|
|
543
|
+
* buildNoticeDeterminations([{ id: 'min', message: 'Minimum applies' }], { id: 'r1' })
|
|
545
544
|
* ```
|
|
546
545
|
*/
|
|
547
|
-
function
|
|
546
|
+
function buildNoticeDeterminations(notices, subject) {
|
|
548
547
|
return notices.map((notice) => ({
|
|
549
548
|
id: notice.id,
|
|
550
549
|
effect: "notice",
|
|
@@ -555,14 +554,14 @@ function buildNotices(notices, subject) {
|
|
|
555
554
|
}));
|
|
556
555
|
}
|
|
557
556
|
/**
|
|
558
|
-
*
|
|
557
|
+
* Converts a logical result's applied rules into `limit` {@link Determination} values.
|
|
559
558
|
*
|
|
560
559
|
* @remarks
|
|
561
560
|
* Fires for both the per-subject authority and the batch aggregate gates — both
|
|
562
|
-
* are plain {@link LogicalDefinition}
|
|
561
|
+
* are plain {@link LogicalDefinition} definitions with no program-authored ruling map, so a
|
|
563
562
|
* fired rule's own `description` (from `@orkestrel/reason`) is the message
|
|
564
563
|
* template, interpolated against the working record the definition ran against.
|
|
565
|
-
* Rich premises reuse the qualifier's {@link
|
|
564
|
+
* Rich premises reuse the qualifier's {@link ruleToPremises}. A rule that never
|
|
566
565
|
* fires produces no determination — program has no authored ruling map to keep
|
|
567
566
|
* evidence for.
|
|
568
567
|
*
|
|
@@ -575,12 +574,12 @@ function buildNotices(notices, subject) {
|
|
|
575
574
|
*
|
|
576
575
|
* @example
|
|
577
576
|
* ```ts
|
|
578
|
-
* import {
|
|
577
|
+
* import { buildLimitDeterminations } from '@orkestrel/program'
|
|
579
578
|
*
|
|
580
|
-
*
|
|
579
|
+
* buildLimitDeterminations(authority, resolved, outcome, evaluator)
|
|
581
580
|
* ```
|
|
582
581
|
*/
|
|
583
|
-
function
|
|
582
|
+
function buildLimitDeterminations(definition, result, working, evaluator, labels) {
|
|
584
583
|
const output = [];
|
|
585
584
|
for (const entry of result.rules) {
|
|
586
585
|
if (!entry.applied) continue;
|
|
@@ -591,13 +590,13 @@ function buildLimits(definition, result, working, evaluator, labels) {
|
|
|
591
590
|
effect: "limit",
|
|
592
591
|
applied: true,
|
|
593
592
|
...rule.description === void 0 ? {} : { message: interpolateMessage(rule.description, working) },
|
|
594
|
-
premises:
|
|
593
|
+
premises: ruleToPremises(rule, working, evaluator, labels)
|
|
595
594
|
});
|
|
596
595
|
}
|
|
597
596
|
return output;
|
|
598
597
|
}
|
|
599
598
|
/**
|
|
600
|
-
*
|
|
599
|
+
* Builds the private authority outcome projection from an assembled program result.
|
|
601
600
|
*
|
|
602
601
|
* @remarks
|
|
603
602
|
* The authority reads this record under {@link OUTCOME_KEY}; it never receives
|
|
@@ -626,7 +625,7 @@ function buildOutcomeProjection(result) {
|
|
|
626
625
|
};
|
|
627
626
|
}
|
|
628
627
|
/**
|
|
629
|
-
*
|
|
628
|
+
* Assembles a {@link ProgramResult} from its qualification, rating, and
|
|
630
629
|
* determination parts — before or after authority.
|
|
631
630
|
*
|
|
632
631
|
* @remarks
|
|
@@ -687,7 +686,7 @@ function buildProgramResult(definition, qualification, rating, determinations, s
|
|
|
687
686
|
};
|
|
688
687
|
}
|
|
689
688
|
/**
|
|
690
|
-
*
|
|
689
|
+
* Adds optional aggregate context to a private subject copy for qualification.
|
|
691
690
|
*
|
|
692
691
|
* @remarks
|
|
693
692
|
* The original subject is returned unchanged when no aggregate context exists.
|
|
@@ -722,7 +721,7 @@ function buildQualificationSubject(subject, aggregate) {
|
|
|
722
721
|
};
|
|
723
722
|
}
|
|
724
723
|
/**
|
|
725
|
-
*
|
|
724
|
+
* Returns authored scopes (qualification ruling scopes or notice scopes) that
|
|
726
725
|
* name no rating line on the program.
|
|
727
726
|
*
|
|
728
727
|
* @remarks
|
|
@@ -749,7 +748,7 @@ function findMissingScopes(definition) {
|
|
|
749
748
|
return [...missing];
|
|
750
749
|
}
|
|
751
750
|
/**
|
|
752
|
-
*
|
|
751
|
+
* Asserts a program definition's always-on construction invariants — missing
|
|
753
752
|
* scope references and duplicate rating-line or notice ids.
|
|
754
753
|
*
|
|
755
754
|
* @remarks
|
|
@@ -758,10 +757,10 @@ function findMissingScopes(definition) {
|
|
|
758
757
|
* an authoring mistake this severe cannot silently compile.
|
|
759
758
|
*
|
|
760
759
|
* @param definition - The program definition to assert
|
|
761
|
-
* @throws {@link ProgramError}
|
|
762
|
-
*
|
|
763
|
-
* @throws {@link ProgramError}
|
|
764
|
-
*
|
|
760
|
+
* @throws {@link ProgramError} Thrown when a ruling or notice scope names no
|
|
761
|
+
* rating line (`'MISSING'`).
|
|
762
|
+
* @throws {@link ProgramError} Thrown when two rating lines or two notices share
|
|
763
|
+
* an id (`'DUPLICATE'`).
|
|
765
764
|
*
|
|
766
765
|
* @example
|
|
767
766
|
* ```ts
|
|
@@ -779,7 +778,7 @@ function assertProgramDefinition(definition) {
|
|
|
779
778
|
if (duplicateNotices.length > 0) throw new ProgramError("DUPLICATE", `Duplicate notice id: ${duplicateNotices.join(", ")}`, definition.id);
|
|
780
779
|
}
|
|
781
780
|
/**
|
|
782
|
-
*
|
|
781
|
+
* Validates a program definition's shape, references, and nested definitions.
|
|
783
782
|
*
|
|
784
783
|
* @remarks
|
|
785
784
|
* The single semantic-validation implementation used by `Program.validate`. It
|
|
@@ -812,7 +811,7 @@ function validateProgramDefinition(definition, qualifier, engine) {
|
|
|
812
811
|
if (definition.id.length === 0) errors.push("Program id must not be empty");
|
|
813
812
|
if (definition.name.length === 0) errors.push("Program name must not be empty");
|
|
814
813
|
const qualification = qualifier.validate(definition.qualification);
|
|
815
|
-
if (
|
|
814
|
+
if (isReasonValidationResult(qualification)) {
|
|
816
815
|
errors.push(...qualification.errors.map((error) => `qualification: ${error}`));
|
|
817
816
|
warnings.push(...qualification.warnings.map((warning) => `qualification: ${warning}`));
|
|
818
817
|
} else errors.push("qualification: Qualifier returned invalid validation result");
|
|
@@ -842,7 +841,7 @@ function validateProgramDefinition(definition, qualifier, engine) {
|
|
|
842
841
|
if (fields.has(key)) errors.push(`Duplicate aggregate field "${key}"`);
|
|
843
842
|
fields.add(key);
|
|
844
843
|
}
|
|
845
|
-
if (aggregate.
|
|
844
|
+
if (aggregate.partition !== void 0 && formatField(aggregate.partition).length === 0) errors.push("Aggregate partition field must be non-empty");
|
|
846
845
|
if (aggregate.gates !== void 0) {
|
|
847
846
|
const validation = engine.validate(aggregate.gates);
|
|
848
847
|
if (isReasonValidationResult(validation)) {
|
|
@@ -860,7 +859,7 @@ function validateProgramDefinition(definition, qualifier, engine) {
|
|
|
860
859
|
};
|
|
861
860
|
}
|
|
862
861
|
/**
|
|
863
|
-
*
|
|
862
|
+
* Coerces a subject's partition-key field to its group-key string.
|
|
864
863
|
*
|
|
865
864
|
* @remarks
|
|
866
865
|
* The key is the resolved field coerced with `String` — `undefined` collapses
|
|
@@ -869,7 +868,7 @@ function validateProgramDefinition(definition, qualifier, engine) {
|
|
|
869
868
|
* collides with the string `'1'`.
|
|
870
869
|
*
|
|
871
870
|
* @param subject - The subject to key
|
|
872
|
-
* @param
|
|
871
|
+
* @param partition - The field the batch partitions on
|
|
873
872
|
* @returns The subject's group key
|
|
874
873
|
*
|
|
875
874
|
* @example
|
|
@@ -879,11 +878,11 @@ function validateProgramDefinition(definition, qualifier, engine) {
|
|
|
879
878
|
* formatGroupKey({ location: 'east' }, 'location') // 'east'
|
|
880
879
|
* ```
|
|
881
880
|
*/
|
|
882
|
-
function formatGroupKey(subject,
|
|
883
|
-
return String(resolveField(subject,
|
|
881
|
+
function formatGroupKey(subject, partition) {
|
|
882
|
+
return String(resolveField(subject, partition) ?? "");
|
|
884
883
|
}
|
|
885
884
|
/**
|
|
886
|
-
*
|
|
885
|
+
* Folds one subject's finite aggregate field values into a sums record.
|
|
887
886
|
*
|
|
888
887
|
* @remarks
|
|
889
888
|
* Returns a FRESH record — `sums` is never mutated. Only finite numbers
|
|
@@ -913,7 +912,7 @@ function sumFields(sums, subject, fields) {
|
|
|
913
912
|
return next;
|
|
914
913
|
}
|
|
915
914
|
/**
|
|
916
|
-
*
|
|
915
|
+
* Sums aggregate fields across a batch of subjects.
|
|
917
916
|
*
|
|
918
917
|
* @remarks
|
|
919
918
|
* A {@link FieldPath} may be nested — a nested path sums a nested subject field
|
|
@@ -933,12 +932,12 @@ function sumFields(sums, subject, fields) {
|
|
|
933
932
|
* ```
|
|
934
933
|
*/
|
|
935
934
|
function aggregateSums(subjects, fields) {
|
|
936
|
-
let sums =
|
|
935
|
+
let sums = buildEmptySums(fields);
|
|
937
936
|
for (const subject of subjects) sums = sumFields(sums, subject, fields);
|
|
938
937
|
return sums;
|
|
939
938
|
}
|
|
940
939
|
/**
|
|
941
|
-
*
|
|
940
|
+
* Partitions a batch of subjects by a field, summing aggregate fields per key.
|
|
942
941
|
*
|
|
943
942
|
* @remarks
|
|
944
943
|
* The partition key is derived by {@link formatGroupKey}. Group order follows
|
|
@@ -946,8 +945,8 @@ function aggregateSums(subjects, fields) {
|
|
|
946
945
|
*
|
|
947
946
|
* @param subjects - The batch of subjects
|
|
948
947
|
* @param fields - The fields to sum within each partition
|
|
949
|
-
* @param
|
|
950
|
-
* @returns A fresh list of aggregate groups, or an empty list when `
|
|
948
|
+
* @param partition - The field the batch partitions on; no partition is built when absent
|
|
949
|
+
* @returns A fresh list of aggregate groups, or an empty list when `partition` is absent
|
|
951
950
|
*
|
|
952
951
|
* @example
|
|
953
952
|
* ```ts
|
|
@@ -956,11 +955,11 @@ function aggregateSums(subjects, fields) {
|
|
|
956
955
|
* aggregateGroups([{ location: 'east', amount: 5 }], ['amount'], 'location')
|
|
957
956
|
* ```
|
|
958
957
|
*/
|
|
959
|
-
function aggregateGroups(subjects, fields,
|
|
960
|
-
if (
|
|
958
|
+
function aggregateGroups(subjects, fields, partition) {
|
|
959
|
+
if (partition === void 0) return [];
|
|
961
960
|
const records = /* @__PURE__ */ new Map();
|
|
962
961
|
for (const subject of subjects) {
|
|
963
|
-
const key = formatGroupKey(subject,
|
|
962
|
+
const key = formatGroupKey(subject, partition);
|
|
964
963
|
const group = records.get(key);
|
|
965
964
|
if (group === void 0) records.set(key, [subject]);
|
|
966
965
|
else group.push(subject);
|
|
@@ -972,7 +971,7 @@ function aggregateGroups(subjects, fields, by) {
|
|
|
972
971
|
}));
|
|
973
972
|
}
|
|
974
973
|
/**
|
|
975
|
-
*
|
|
974
|
+
* Builds one subject's overall and optional group aggregate projection.
|
|
976
975
|
*
|
|
977
976
|
* @remarks
|
|
978
977
|
* The projection carries the whole-batch `count` and `sums` plus the subject's
|
|
@@ -983,7 +982,7 @@ function aggregateGroups(subjects, fields, by) {
|
|
|
983
982
|
* @param count - The whole-batch subject count
|
|
984
983
|
* @param sums - The whole-batch summed aggregate fields
|
|
985
984
|
* @param groups - The batch partitions
|
|
986
|
-
* @param
|
|
985
|
+
* @param partition - The field the batch partitions on; no group is attached when absent
|
|
987
986
|
* @returns A fresh aggregate projection
|
|
988
987
|
*
|
|
989
988
|
* @example
|
|
@@ -993,8 +992,8 @@ function aggregateGroups(subjects, fields, by) {
|
|
|
993
992
|
* buildAggregateProjection(subject, 2, { amount: 8 }, groups, 'location')
|
|
994
993
|
* ```
|
|
995
994
|
*/
|
|
996
|
-
function buildAggregateProjection(subject, count, sums, groups,
|
|
997
|
-
const group =
|
|
995
|
+
function buildAggregateProjection(subject, count, sums, groups, partition) {
|
|
996
|
+
const group = partition === void 0 ? void 0 : groups.find((entry) => entry.key === formatGroupKey(subject, partition));
|
|
998
997
|
return {
|
|
999
998
|
count,
|
|
1000
999
|
sums: { ...sums },
|
|
@@ -1002,7 +1001,7 @@ function buildAggregateProjection(subject, count, sums, groups, by) {
|
|
|
1002
1001
|
};
|
|
1003
1002
|
}
|
|
1004
1003
|
/**
|
|
1005
|
-
*
|
|
1004
|
+
* Builds the reserved-key record a batch aggregate-gate definition runs against.
|
|
1006
1005
|
*
|
|
1007
1006
|
* @remarks
|
|
1008
1007
|
* Unlike a per-subject {@link buildAggregateProjection}, the batch record carries
|
|
@@ -1029,29 +1028,29 @@ function buildAggregateRecord(count, sums, groups) {
|
|
|
1029
1028
|
} };
|
|
1030
1029
|
}
|
|
1031
1030
|
/**
|
|
1032
|
-
*
|
|
1031
|
+
* Builds a zero-sum record for a set of aggregate fields.
|
|
1033
1032
|
*
|
|
1034
1033
|
* @param fields - The fields to zero
|
|
1035
1034
|
* @returns A fresh record of dot-joined field to `0`
|
|
1036
1035
|
*
|
|
1037
1036
|
* @example
|
|
1038
1037
|
* ```ts
|
|
1039
|
-
* import {
|
|
1038
|
+
* import { buildEmptySums } from '@orkestrel/program'
|
|
1040
1039
|
*
|
|
1041
|
-
*
|
|
1040
|
+
* buildEmptySums(['amount']) // { amount: 0 }
|
|
1042
1041
|
* ```
|
|
1043
1042
|
*/
|
|
1044
|
-
function
|
|
1043
|
+
function buildEmptySums(fields) {
|
|
1045
1044
|
const sums = {};
|
|
1046
1045
|
for (const field of fields) sums[formatField(field)] = 0;
|
|
1047
1046
|
return sums;
|
|
1048
1047
|
}
|
|
1049
1048
|
/**
|
|
1050
|
-
*
|
|
1049
|
+
* Completes a partial status tally record with zero entries for every missing
|
|
1051
1050
|
* {@link Status}.
|
|
1052
1051
|
*
|
|
1053
1052
|
* @param entries - The partial tally entries to complete
|
|
1054
|
-
* @returns A record
|
|
1053
|
+
* @returns A record carrying every {@link Status}
|
|
1055
1054
|
*
|
|
1056
1055
|
* @example
|
|
1057
1056
|
* ```ts
|
|
@@ -1085,28 +1084,28 @@ function completeTallies(entries) {
|
|
|
1085
1084
|
};
|
|
1086
1085
|
}
|
|
1087
1086
|
/**
|
|
1088
|
-
*
|
|
1087
|
+
* Builds complete zero status tallies in {@link STATUSES} order.
|
|
1089
1088
|
*
|
|
1090
1089
|
* @param fields - The fields each tally's sums are zeroed for
|
|
1091
1090
|
* @returns A fresh, complete tally record
|
|
1092
1091
|
*
|
|
1093
1092
|
* @example
|
|
1094
1093
|
* ```ts
|
|
1095
|
-
* import {
|
|
1094
|
+
* import { buildEmptyTallies } from '@orkestrel/program'
|
|
1096
1095
|
*
|
|
1097
|
-
*
|
|
1096
|
+
* buildEmptyTallies(['amount'])
|
|
1098
1097
|
* ```
|
|
1099
1098
|
*/
|
|
1100
|
-
function
|
|
1099
|
+
function buildEmptyTallies(fields) {
|
|
1101
1100
|
const entries = {};
|
|
1102
|
-
for (const status of
|
|
1101
|
+
for (const status of STATUSES) entries[status] = {
|
|
1103
1102
|
count: 0,
|
|
1104
|
-
sums:
|
|
1103
|
+
sums: buildEmptySums(fields)
|
|
1105
1104
|
};
|
|
1106
1105
|
return completeTallies(entries);
|
|
1107
1106
|
}
|
|
1108
1107
|
/**
|
|
1109
|
-
*
|
|
1108
|
+
* Adds one subject's aggregate contribution to a status tally record.
|
|
1110
1109
|
*
|
|
1111
1110
|
* @param tallies - The tallies to update
|
|
1112
1111
|
* @param result - The subject's program result (its `status` selects the tally)
|
|
@@ -1116,12 +1115,12 @@ function emptyTallies(fields) {
|
|
|
1116
1115
|
*
|
|
1117
1116
|
* @example
|
|
1118
1117
|
* ```ts
|
|
1119
|
-
* import {
|
|
1118
|
+
* import { tallySubject } from '@orkestrel/program'
|
|
1120
1119
|
*
|
|
1121
|
-
*
|
|
1120
|
+
* tallySubject(tallies, result, { id: 'r1', amount: 5 }, ['amount'])
|
|
1122
1121
|
* ```
|
|
1123
1122
|
*/
|
|
1124
|
-
function
|
|
1123
|
+
function tallySubject(tallies, result, subject, fields) {
|
|
1125
1124
|
const status = result.status;
|
|
1126
1125
|
const current = tallies[status];
|
|
1127
1126
|
const sums = sumFields(current.sums, subject, fields);
|
|
@@ -1134,7 +1133,7 @@ function tallyProgram(tallies, result, subject, fields) {
|
|
|
1134
1133
|
});
|
|
1135
1134
|
}
|
|
1136
1135
|
/**
|
|
1137
|
-
*
|
|
1136
|
+
* Assembles one batch {@link AggregateResult} from its per-subject and aggregate
|
|
1138
1137
|
* parts.
|
|
1139
1138
|
*
|
|
1140
1139
|
* @remarks
|
|
@@ -1180,11 +1179,14 @@ function buildAggregateResult(definition, subjects, determinations, groups, tall
|
|
|
1180
1179
|
};
|
|
1181
1180
|
}
|
|
1182
1181
|
/**
|
|
1183
|
-
*
|
|
1182
|
+
* Builds a fresh {@link ProgramDefinition}.
|
|
1184
1183
|
*
|
|
1185
1184
|
* @remarks
|
|
1186
|
-
*
|
|
1187
|
-
*
|
|
1185
|
+
* Omits absent optional keys. `metadata` is deep-copied with `structuredClone`.
|
|
1186
|
+
* `notices` is copied as a fresh array whose elements are shared with the
|
|
1187
|
+
* input. `qualification`, `rating`, `authority`, and `aggregate` are stored
|
|
1188
|
+
* by reference. The {@link Program} constructor later snapshots and seals
|
|
1189
|
+
* the whole graph.
|
|
1188
1190
|
*
|
|
1189
1191
|
* @param id - The program id
|
|
1190
1192
|
* @param name - The display name
|
|
@@ -1195,12 +1197,12 @@ function buildAggregateResult(definition, subjects, determinations, groups, tall
|
|
|
1195
1197
|
*
|
|
1196
1198
|
* @example
|
|
1197
1199
|
* ```ts
|
|
1198
|
-
* import {
|
|
1200
|
+
* import { buildProgramDefinition } from '@orkestrel/program'
|
|
1199
1201
|
*
|
|
1200
|
-
*
|
|
1202
|
+
* buildProgramDefinition('standard', 'Standard', qualification, rating, { notices: [notice] })
|
|
1201
1203
|
* ```
|
|
1202
1204
|
*/
|
|
1203
|
-
function
|
|
1205
|
+
function buildProgramDefinition(id, name, qualification, rating, input) {
|
|
1204
1206
|
return {
|
|
1205
1207
|
id,
|
|
1206
1208
|
name,
|
|
@@ -1210,25 +1212,25 @@ function programDefinition(id, name, qualification, rating, input) {
|
|
|
1210
1212
|
...input?.notices === void 0 ? {} : { notices: [...input.notices] },
|
|
1211
1213
|
...input?.authority === void 0 ? {} : { authority: input.authority },
|
|
1212
1214
|
...input?.aggregate === void 0 ? {} : { aggregate: input.aggregate },
|
|
1213
|
-
...input?.metadata === void 0 ? {} : { metadata:
|
|
1215
|
+
...input?.metadata === void 0 ? {} : { metadata: structuredClone(input.metadata) }
|
|
1214
1216
|
};
|
|
1215
1217
|
}
|
|
1216
1218
|
/**
|
|
1217
|
-
*
|
|
1219
|
+
* Builds a fresh {@link Notice}.
|
|
1218
1220
|
*
|
|
1219
1221
|
* @param id - The notice id
|
|
1220
|
-
* @param message - The message template, carrying optional `{{token}}`
|
|
1222
|
+
* @param message - The message template, carrying optional `{{token}}` placeholders
|
|
1221
1223
|
* @param input - Optional presentation scope
|
|
1222
1224
|
* @returns A fresh notice
|
|
1223
1225
|
*
|
|
1224
1226
|
* @example
|
|
1225
1227
|
* ```ts
|
|
1226
|
-
* import {
|
|
1228
|
+
* import { buildNotice } from '@orkestrel/program'
|
|
1227
1229
|
*
|
|
1228
|
-
*
|
|
1230
|
+
* buildNotice('minimum', 'Minimum earned premium applies')
|
|
1229
1231
|
* ```
|
|
1230
1232
|
*/
|
|
1231
|
-
function
|
|
1233
|
+
function buildNotice(id, message, input) {
|
|
1232
1234
|
return {
|
|
1233
1235
|
id,
|
|
1234
1236
|
message,
|
|
@@ -1236,7 +1238,7 @@ function noticeDefinition(id, message, input) {
|
|
|
1236
1238
|
};
|
|
1237
1239
|
}
|
|
1238
1240
|
/**
|
|
1239
|
-
*
|
|
1241
|
+
* Builds a fresh {@link AggregateDefinition}.
|
|
1240
1242
|
*
|
|
1241
1243
|
* @param fields - The aggregate fields to sum across a batch
|
|
1242
1244
|
* @param input - Optional partition field and aggregate gates
|
|
@@ -1244,23 +1246,23 @@ function noticeDefinition(id, message, input) {
|
|
|
1244
1246
|
*
|
|
1245
1247
|
* @example
|
|
1246
1248
|
* ```ts
|
|
1247
|
-
* import {
|
|
1249
|
+
* import { buildAggregateDefinition } from '@orkestrel/program'
|
|
1248
1250
|
*
|
|
1249
|
-
*
|
|
1251
|
+
* buildAggregateDefinition(['amount'], { partition: 'location' })
|
|
1250
1252
|
* ```
|
|
1251
1253
|
*/
|
|
1252
|
-
function
|
|
1254
|
+
function buildAggregateDefinition(fields, input) {
|
|
1253
1255
|
return {
|
|
1254
1256
|
fields: [...fields],
|
|
1255
|
-
...input?.
|
|
1257
|
+
...input?.partition === void 0 ? {} : { partition: input.partition },
|
|
1256
1258
|
...input?.gates === void 0 ? {} : { gates: input.gates }
|
|
1257
1259
|
};
|
|
1258
1260
|
}
|
|
1259
1261
|
//#endregion
|
|
1260
1262
|
//#region src/core/programs/Program.ts
|
|
1261
1263
|
/**
|
|
1262
|
-
*
|
|
1263
|
-
*
|
|
1264
|
+
* Composes one qualifier and one rater over a shared reason engine and executes
|
|
1265
|
+
* single subjects or aggregate-aware batches.
|
|
1264
1266
|
*
|
|
1265
1267
|
* @remarks
|
|
1266
1268
|
* Qualification decides whether rating happens: a globally ineligible, referred,
|
|
@@ -1293,21 +1295,31 @@ var Program = class {
|
|
|
1293
1295
|
#validate;
|
|
1294
1296
|
#labels;
|
|
1295
1297
|
#destroyed = false;
|
|
1298
|
+
/** Holds the authored id of the definition this program compiled. */
|
|
1296
1299
|
id;
|
|
1300
|
+
/** Holds the authored display name of the definition this program compiled. */
|
|
1297
1301
|
name;
|
|
1302
|
+
/** Holds the sealed snapshot of the authored definition this program compiled. */
|
|
1298
1303
|
definition;
|
|
1304
|
+
/**
|
|
1305
|
+
* Compiles one program from an authored definition.
|
|
1306
|
+
*
|
|
1307
|
+
* @param definition - The authored program definition
|
|
1308
|
+
* @param options - Optional injected qualifier, rater, engine, validation, labels, and emitter hooks
|
|
1309
|
+
* @throws {@link ProgramError} Thrown when the definition cannot be cloned or
|
|
1310
|
+
* sealed, or when validation is enabled and the definition fails
|
|
1311
|
+
* (`'DEFINITION'`).
|
|
1312
|
+
* @throws {@link ProgramError} Thrown when a ruling or notice scope names no
|
|
1313
|
+
* rating line (`'MISSING'`).
|
|
1314
|
+
* @throws {@link ProgramError} Thrown when the definition repeats a rating-line
|
|
1315
|
+
* or notice id (`'DUPLICATE'`).
|
|
1316
|
+
*/
|
|
1299
1317
|
constructor(definition, options) {
|
|
1300
1318
|
let snapshot;
|
|
1301
1319
|
try {
|
|
1302
1320
|
snapshot = structuredClone(definition);
|
|
1303
1321
|
} catch (cause) {
|
|
1304
|
-
|
|
1305
|
-
Object.defineProperty(error, "cause", {
|
|
1306
|
-
configurable: true,
|
|
1307
|
-
value: cause,
|
|
1308
|
-
writable: true
|
|
1309
|
-
});
|
|
1310
|
-
throw error;
|
|
1322
|
+
throw new ProgramError("DEFINITION", "Program definition could not be cloned", void 0, cause);
|
|
1311
1323
|
}
|
|
1312
1324
|
assertProgramDefinition(snapshot);
|
|
1313
1325
|
this.id = snapshot.id;
|
|
@@ -1316,13 +1328,7 @@ var Program = class {
|
|
|
1316
1328
|
try {
|
|
1317
1329
|
this.#seal();
|
|
1318
1330
|
} catch (cause) {
|
|
1319
|
-
|
|
1320
|
-
Object.defineProperty(error, "cause", {
|
|
1321
|
-
configurable: true,
|
|
1322
|
-
value: cause,
|
|
1323
|
-
writable: true
|
|
1324
|
-
});
|
|
1325
|
-
throw error;
|
|
1331
|
+
throw new ProgramError("DEFINITION", "Program definition could not be sealed", snapshot.id, cause);
|
|
1326
1332
|
}
|
|
1327
1333
|
this.#emitter = new Emitter({
|
|
1328
1334
|
...options?.on === void 0 ? {} : { on: options.on },
|
|
@@ -1348,6 +1354,21 @@ var Program = class {
|
|
|
1348
1354
|
}
|
|
1349
1355
|
}
|
|
1350
1356
|
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Holds the typed observation surface carrying `qualify`, `rate`, `determine`,
|
|
1359
|
+
* `decide`, `execute`, `aggregate`, and `destroy`.
|
|
1360
|
+
*
|
|
1361
|
+
* @returns The emitter this program owns
|
|
1362
|
+
*
|
|
1363
|
+
* @example
|
|
1364
|
+
* ```ts
|
|
1365
|
+
* import { createProgram } from '@orkestrel/program'
|
|
1366
|
+
*
|
|
1367
|
+
* const program = createProgram(definition)
|
|
1368
|
+
* program.emitter.on('execute', (result) => result.status)
|
|
1369
|
+
* program.destroy()
|
|
1370
|
+
* ```
|
|
1371
|
+
*/
|
|
1351
1372
|
get emitter() {
|
|
1352
1373
|
return this.#emitter;
|
|
1353
1374
|
}
|
|
@@ -1356,10 +1377,51 @@ var Program = class {
|
|
|
1356
1377
|
if (isArray(input)) return this.#aggregate(input);
|
|
1357
1378
|
return this.#subject(input);
|
|
1358
1379
|
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Validates this program's definition and every nested definition.
|
|
1382
|
+
*
|
|
1383
|
+
* @remarks
|
|
1384
|
+
* Exact shape is `isProgramDefinition`'s job. This checks the meaning: non-empty id
|
|
1385
|
+
* and name, every ruling and notice scope naming a rating line, unique non-empty
|
|
1386
|
+
* aggregate fields, and a non-empty partition field when present. Nested
|
|
1387
|
+
* qualification validation is delegated to the injected qualifier, and authority
|
|
1388
|
+
* and aggregate-gate validation to the shared reason engine.
|
|
1389
|
+
*
|
|
1390
|
+
* @returns A fresh validation result carrying `valid`, `errors`, and `warnings`
|
|
1391
|
+
* @throws {@link ProgramError} Thrown when the program has been destroyed
|
|
1392
|
+
* (`'DESTROYED'`).
|
|
1393
|
+
*
|
|
1394
|
+
* @example
|
|
1395
|
+
* ```ts
|
|
1396
|
+
* import { createProgram } from '@orkestrel/program'
|
|
1397
|
+
*
|
|
1398
|
+
* const program = createProgram(definition, { validate: false })
|
|
1399
|
+
* program.validate().valid // true
|
|
1400
|
+
* program.destroy()
|
|
1401
|
+
* ```
|
|
1402
|
+
*/
|
|
1359
1403
|
validate() {
|
|
1360
1404
|
this.#alive();
|
|
1361
1405
|
return validateProgramDefinition(this.definition, this.#qualifier, this.#engine);
|
|
1362
1406
|
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Destroys this program, idempotently.
|
|
1409
|
+
*
|
|
1410
|
+
* @remarks
|
|
1411
|
+
* The destroyed flag is set BEFORE any teardown or the `destroy` event, so a
|
|
1412
|
+
* listener re-entering `destroy` is a no-op. An owned qualifier, rater, and reason
|
|
1413
|
+
* engine are destroyed; an injected one stays caller-owned. The emitter is torn
|
|
1414
|
+
* down last, and stays reachable afterwards.
|
|
1415
|
+
*
|
|
1416
|
+
* @example
|
|
1417
|
+
* ```ts
|
|
1418
|
+
* import { createProgram } from '@orkestrel/program'
|
|
1419
|
+
*
|
|
1420
|
+
* const program = createProgram(definition)
|
|
1421
|
+
* program.destroy()
|
|
1422
|
+
* program.destroy() // a second call is a no-op
|
|
1423
|
+
* ```
|
|
1424
|
+
*/
|
|
1363
1425
|
destroy() {
|
|
1364
1426
|
if (this.#destroyed) return;
|
|
1365
1427
|
this.#destroyed = true;
|
|
@@ -1385,7 +1447,7 @@ var Program = class {
|
|
|
1385
1447
|
return this.#finish(subject, qualification, rating);
|
|
1386
1448
|
}
|
|
1387
1449
|
#finish(subject, qualification, rating) {
|
|
1388
|
-
const notices =
|
|
1450
|
+
const notices = buildNoticeDeterminations(this.definition.notices ?? [], subject);
|
|
1389
1451
|
for (const notice of notices) this.#emitter.emit("determine", notice);
|
|
1390
1452
|
const status = deriveStatus(this.definition, qualification, rating);
|
|
1391
1453
|
let result = buildProgramResult(this.definition, qualification, rating, notices, status);
|
|
@@ -1397,7 +1459,7 @@ var Program = class {
|
|
|
1397
1459
|
const outcome = { [OUTCOME_KEY]: buildOutcomeProjection(result) };
|
|
1398
1460
|
const resolved = this.#engine.reason(outcome, authority);
|
|
1399
1461
|
if (!isLogicalResult(resolved)) throw new ProgramError("MISMATCH", "Authority returned invalid logical result", authority.id);
|
|
1400
|
-
const limits =
|
|
1462
|
+
const limits = buildLimitDeterminations(authority, resolved, outcome, this.#evaluator, this.#labels);
|
|
1401
1463
|
for (const limit of limits) this.#emitter.emit("determine", limit);
|
|
1402
1464
|
result = buildProgramResult(this.definition, qualification, rating, [...notices, ...limits], status, { authority: resolved });
|
|
1403
1465
|
if (result.decision !== void 0) this.#emitter.emit("decide", result.decision, result);
|
|
@@ -1409,12 +1471,12 @@ var Program = class {
|
|
|
1409
1471
|
const definition = this.definition.aggregate;
|
|
1410
1472
|
const fields = [...definition?.fields ?? []];
|
|
1411
1473
|
const sums = aggregateSums(subjects, fields);
|
|
1412
|
-
const groups = aggregateGroups(subjects, fields, definition?.
|
|
1413
|
-
let tallies =
|
|
1474
|
+
const groups = aggregateGroups(subjects, fields, definition?.partition);
|
|
1475
|
+
let tallies = buildEmptyTallies(fields);
|
|
1414
1476
|
const results = subjects.map((subject) => {
|
|
1415
|
-
const projection = definition === void 0 ? void 0 : buildAggregateProjection(subject, subjects.length, sums, groups, definition.
|
|
1477
|
+
const projection = definition === void 0 ? void 0 : buildAggregateProjection(subject, subjects.length, sums, groups, definition.partition);
|
|
1416
1478
|
const result = this.#subject(subject, projection);
|
|
1417
|
-
tallies =
|
|
1479
|
+
tallies = tallySubject(tallies, result, subject, fields);
|
|
1418
1480
|
return result;
|
|
1419
1481
|
});
|
|
1420
1482
|
const gates = this.#aggregateLimits(subjects.length, sums, groups);
|
|
@@ -1428,7 +1490,7 @@ var Program = class {
|
|
|
1428
1490
|
const record = buildAggregateRecord(count, sums, groups);
|
|
1429
1491
|
const resolved = this.#engine.reason(record, gates);
|
|
1430
1492
|
if (!isLogicalResult(resolved)) throw new ProgramError("MISMATCH", "Aggregate gates returned invalid logical result", gates.id);
|
|
1431
|
-
const determinations =
|
|
1493
|
+
const determinations = buildLimitDeterminations(gates, resolved, record, this.#evaluator, this.#labels);
|
|
1432
1494
|
for (const determination of determinations) this.#emitter.emit("determine", determination);
|
|
1433
1495
|
return {
|
|
1434
1496
|
determinations,
|
|
@@ -1451,8 +1513,8 @@ var Program = class {
|
|
|
1451
1513
|
//#endregion
|
|
1452
1514
|
//#region src/core/programs/ProgramManager.ts
|
|
1453
1515
|
/**
|
|
1454
|
-
*
|
|
1455
|
-
*
|
|
1516
|
+
* Manages compiled {@link ProgramInterface} programs in order, sharing one
|
|
1517
|
+
* qualifier, rater, and reason engine across every program it compiles.
|
|
1456
1518
|
*
|
|
1457
1519
|
* @remarks
|
|
1458
1520
|
* OWNS its ordered `#programs` collection and its own {@link Emitter} over
|
|
@@ -1479,6 +1541,13 @@ var ProgramManager = class {
|
|
|
1479
1541
|
#validate;
|
|
1480
1542
|
#labels;
|
|
1481
1543
|
#destroyed = false;
|
|
1544
|
+
/**
|
|
1545
|
+
* Creates one manager and compiles every seed definition in order.
|
|
1546
|
+
*
|
|
1547
|
+
* @param options - Optional injected qualifier, rater, engine, seed programs, validation, labels, and emitter hooks
|
|
1548
|
+
* @throws {@link ProgramError} Thrown when a seed definition fails to compile,
|
|
1549
|
+
* after the manager destroys whatever it had already compiled.
|
|
1550
|
+
*/
|
|
1482
1551
|
constructor(options) {
|
|
1483
1552
|
this.#emitter = new Emitter({
|
|
1484
1553
|
...options?.on === void 0 ? {} : { on: options.on },
|
|
@@ -1502,25 +1571,138 @@ var ProgramManager = class {
|
|
|
1502
1571
|
throw error;
|
|
1503
1572
|
}
|
|
1504
1573
|
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Holds the typed observation surface carrying `add`, `remove`, and `destroy`.
|
|
1576
|
+
*
|
|
1577
|
+
* @returns The emitter this manager owns
|
|
1578
|
+
*
|
|
1579
|
+
* @example
|
|
1580
|
+
* ```ts
|
|
1581
|
+
* import { createProgramManager } from '@orkestrel/program'
|
|
1582
|
+
*
|
|
1583
|
+
* const manager = createProgramManager()
|
|
1584
|
+
* manager.emitter.on('add', (id) => id)
|
|
1585
|
+
* manager.destroy()
|
|
1586
|
+
* ```
|
|
1587
|
+
*/
|
|
1505
1588
|
get emitter() {
|
|
1506
1589
|
return this.#emitter;
|
|
1507
1590
|
}
|
|
1508
|
-
|
|
1591
|
+
/**
|
|
1592
|
+
* Holds how many programs the manager has compiled.
|
|
1593
|
+
*
|
|
1594
|
+
* @returns The number of compiled programs
|
|
1595
|
+
* @throws {@link ProgramError} Thrown when the manager has been destroyed
|
|
1596
|
+
* (`'DESTROYED'`).
|
|
1597
|
+
*
|
|
1598
|
+
* @example
|
|
1599
|
+
* ```ts
|
|
1600
|
+
* import { createProgramManager } from '@orkestrel/program'
|
|
1601
|
+
*
|
|
1602
|
+
* const manager = createProgramManager({ programs: [definition] })
|
|
1603
|
+
* manager.count // 1
|
|
1604
|
+
* manager.destroy()
|
|
1605
|
+
* ```
|
|
1606
|
+
*/
|
|
1607
|
+
get count() {
|
|
1509
1608
|
this.#alive();
|
|
1510
1609
|
return this.#programs.length;
|
|
1511
1610
|
}
|
|
1611
|
+
/**
|
|
1612
|
+
* Reports whether an id names a compiled program.
|
|
1613
|
+
*
|
|
1614
|
+
* @param id - The program id to look for
|
|
1615
|
+
* @returns True if a compiled program carries the id; false otherwise
|
|
1616
|
+
* @throws {@link ProgramError} Thrown when the manager has been destroyed
|
|
1617
|
+
* (`'DESTROYED'`).
|
|
1618
|
+
*
|
|
1619
|
+
* @example
|
|
1620
|
+
* ```ts
|
|
1621
|
+
* import { createProgramManager } from '@orkestrel/program'
|
|
1622
|
+
*
|
|
1623
|
+
* const manager = createProgramManager({ programs: [definition] })
|
|
1624
|
+
* manager.has('standard') // true
|
|
1625
|
+
* manager.destroy()
|
|
1626
|
+
* ```
|
|
1627
|
+
*/
|
|
1512
1628
|
has(id) {
|
|
1513
1629
|
this.#alive();
|
|
1514
1630
|
return this.#programs.some((program) => program.id === id);
|
|
1515
1631
|
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Looks one compiled program up by id.
|
|
1634
|
+
*
|
|
1635
|
+
* @param id - The program id to look up
|
|
1636
|
+
* @returns The compiled program, or `undefined` when no program carries the id
|
|
1637
|
+
* @throws {@link ProgramError} Thrown when the manager has been destroyed
|
|
1638
|
+
* (`'DESTROYED'`).
|
|
1639
|
+
*
|
|
1640
|
+
* @example
|
|
1641
|
+
* ```ts
|
|
1642
|
+
* import { createProgramManager } from '@orkestrel/program'
|
|
1643
|
+
*
|
|
1644
|
+
* const manager = createProgramManager({ programs: [definition] })
|
|
1645
|
+
* manager.program('standard')?.execute({ id: 'risk-1', licensed: true })
|
|
1646
|
+
* manager.destroy()
|
|
1647
|
+
* ```
|
|
1648
|
+
*/
|
|
1516
1649
|
program(id) {
|
|
1517
1650
|
this.#alive();
|
|
1518
1651
|
return this.#programs.find((program) => program.id === id);
|
|
1519
1652
|
}
|
|
1653
|
+
/**
|
|
1654
|
+
* Returns every compiled program, in insertion order.
|
|
1655
|
+
*
|
|
1656
|
+
* @remarks
|
|
1657
|
+
* The returned array is a fresh copy, so mutating it never reaches the manager's
|
|
1658
|
+
* own collection.
|
|
1659
|
+
*
|
|
1660
|
+
* @returns A fresh array of compiled programs, in insertion order
|
|
1661
|
+
* @throws {@link ProgramError} Thrown when the manager has been destroyed
|
|
1662
|
+
* (`'DESTROYED'`).
|
|
1663
|
+
*
|
|
1664
|
+
* @example
|
|
1665
|
+
* ```ts
|
|
1666
|
+
* import { createProgramManager } from '@orkestrel/program'
|
|
1667
|
+
*
|
|
1668
|
+
* const manager = createProgramManager({ programs: [definition] })
|
|
1669
|
+
* manager.programs().map((program) => program.id) // ['standard']
|
|
1670
|
+
* manager.destroy()
|
|
1671
|
+
* ```
|
|
1672
|
+
*/
|
|
1520
1673
|
programs() {
|
|
1521
1674
|
this.#alive();
|
|
1522
1675
|
return [...this.#programs];
|
|
1523
1676
|
}
|
|
1677
|
+
/**
|
|
1678
|
+
* Compiles one definition and appends it to the collection.
|
|
1679
|
+
*
|
|
1680
|
+
* @remarks
|
|
1681
|
+
* The compiled program borrows the manager's shared qualifier, rater, and reason
|
|
1682
|
+
* engine, and inherits the manager's `validate` and `labels` options. After
|
|
1683
|
+
* appending the program, the `add` event fires with its id.
|
|
1684
|
+
*
|
|
1685
|
+
* @param definition - The authored program definition to compile
|
|
1686
|
+
* @returns The compiled program
|
|
1687
|
+
* @throws {@link ProgramError} Thrown when the manager has been destroyed
|
|
1688
|
+
* (`'DESTROYED'`).
|
|
1689
|
+
* @throws {@link ProgramError} Thrown when the manager already carries the
|
|
1690
|
+
* definition's id, or the definition repeats a rating-line or notice id
|
|
1691
|
+
* (`'DUPLICATE'`).
|
|
1692
|
+
* @throws {@link ProgramError} Thrown when a ruling or notice scope names no
|
|
1693
|
+
* rating line (`'MISSING'`).
|
|
1694
|
+
* @throws {@link ProgramError} Thrown when validation is enabled and the
|
|
1695
|
+
* definition fails (`'DEFINITION'`).
|
|
1696
|
+
*
|
|
1697
|
+
* @example
|
|
1698
|
+
* ```ts
|
|
1699
|
+
* import { createProgramManager } from '@orkestrel/program'
|
|
1700
|
+
*
|
|
1701
|
+
* const manager = createProgramManager()
|
|
1702
|
+
* manager.add(definition).id // 'standard'
|
|
1703
|
+
* manager.destroy()
|
|
1704
|
+
* ```
|
|
1705
|
+
*/
|
|
1524
1706
|
add(definition) {
|
|
1525
1707
|
this.#alive();
|
|
1526
1708
|
if (this.has(definition.id)) throw new ProgramError("DUPLICATE", `Program "${definition.id}" already exists`, definition.id);
|
|
@@ -1548,6 +1730,25 @@ var ProgramManager = class {
|
|
|
1548
1730
|
}
|
|
1549
1731
|
if (typeof input === "string") return this.#removeOne(input);
|
|
1550
1732
|
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Destroys this manager, idempotently.
|
|
1735
|
+
*
|
|
1736
|
+
* @remarks
|
|
1737
|
+
* The destroyed flag is set BEFORE any teardown or the `remove` and `destroy`
|
|
1738
|
+
* events, so a `remove` listener re-entering `destroy` is a no-op. Compiled
|
|
1739
|
+
* programs are destroyed first, then an owned qualifier, rater, and reason engine;
|
|
1740
|
+
* an injected one stays caller-owned. The emitter is torn down last, and stays
|
|
1741
|
+
* reachable afterwards.
|
|
1742
|
+
*
|
|
1743
|
+
* @example
|
|
1744
|
+
* ```ts
|
|
1745
|
+
* import { createProgramManager } from '@orkestrel/program'
|
|
1746
|
+
*
|
|
1747
|
+
* const manager = createProgramManager({ programs: [definition] })
|
|
1748
|
+
* manager.destroy()
|
|
1749
|
+
* manager.destroy() // a second call is a no-op
|
|
1750
|
+
* ```
|
|
1751
|
+
*/
|
|
1551
1752
|
destroy() {
|
|
1552
1753
|
if (this.#destroyed) return;
|
|
1553
1754
|
this.#destroyed = true;
|
|
@@ -1580,11 +1781,12 @@ var ProgramManager = class {
|
|
|
1580
1781
|
//#endregion
|
|
1581
1782
|
//#region src/core/factories.ts
|
|
1582
1783
|
/**
|
|
1583
|
-
*
|
|
1784
|
+
* Creates one compiled program over a qualifier and rater.
|
|
1584
1785
|
*
|
|
1585
1786
|
* @remarks
|
|
1586
|
-
*
|
|
1587
|
-
*
|
|
1787
|
+
* If `options.validate` is `true`, the program validates the definition at
|
|
1788
|
+
* construction; if `false`, it compiles the definition unvalidated. Default:
|
|
1789
|
+
* {@link DEFAULT_PROGRAM_VALIDATE}. A standalone program creates and
|
|
1588
1790
|
* OWNS one shared quantitative-plus-logical reason engine and injects it into the
|
|
1589
1791
|
* qualifier and rater it creates; injected dependencies remain caller-owned.
|
|
1590
1792
|
*
|
|
@@ -1594,9 +1796,10 @@ var ProgramManager = class {
|
|
|
1594
1796
|
*
|
|
1595
1797
|
* @example
|
|
1596
1798
|
* ```ts
|
|
1597
|
-
* import {
|
|
1799
|
+
* import { buildProgramDefinition, createProgram } from '@orkestrel/program'
|
|
1598
1800
|
*
|
|
1599
|
-
* const
|
|
1801
|
+
* const definition = buildProgramDefinition('standard', 'Standard', qualification, rating)
|
|
1802
|
+
* const program = createProgram(definition)
|
|
1600
1803
|
* program.execute({ id: 'risk-1' })
|
|
1601
1804
|
* program.destroy()
|
|
1602
1805
|
* ```
|
|
@@ -1605,7 +1808,7 @@ function createProgram(definition, options) {
|
|
|
1605
1808
|
return new Program(definition, options);
|
|
1606
1809
|
}
|
|
1607
1810
|
/**
|
|
1608
|
-
*
|
|
1811
|
+
* Creates one ordered manager over compiled programs.
|
|
1609
1812
|
*
|
|
1610
1813
|
* @remarks
|
|
1611
1814
|
* Creates or borrows one shared reason engine, qualifier, and rater and injects
|
|
@@ -1628,6 +1831,6 @@ function createProgramManager(options) {
|
|
|
1628
1831
|
return new ProgramManager(options);
|
|
1629
1832
|
}
|
|
1630
1833
|
//#endregion
|
|
1631
|
-
export { AGGREGATE_KEY, DEFAULT_PROGRAM_VALIDATE, ELIGIBILITY_DECISIONS, OUTCOME_KEY, Program, ProgramError, ProgramManager,
|
|
1834
|
+
export { AGGREGATE_KEY, DEFAULT_PROGRAM_VALIDATE, ELIGIBILITY_DECISIONS, OUTCOME_KEY, Program, ProgramError, ProgramManager, STATUSES, aggregateGroups, aggregateSums, assertProgramDefinition, assertProgramSubject, buildAggregateDefinition, buildAggregateProjection, buildAggregateRecord, buildAggregateResult, buildEmptySums, buildEmptyTallies, buildLimitDeterminations, buildNotice, buildNoticeDeterminations, buildOutcomeProjection, buildProgramDefinition, buildProgramResult, buildQualificationSubject, completeTallies, createProgram, createProgramManager, decideEligibility, deriveStatus, findMissingScopes, formatGroupKey, hasReservedKey, isAggregateDefinition, isAggregateGroup, isAggregateResult, isDecision, isDetermination, isNotice, isProgramDefinition, isProgramEffect, isProgramError, isProgramResult, isProgramSums, isProgramValidationResult, isStatus, isTallies, isTally, selectProgramLines, sumFields, tallySubject, validateProgramDefinition };
|
|
1632
1835
|
|
|
1633
1836
|
//# sourceMappingURL=index.js.map
|