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