@orkestrel/program 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,17 +1,20 @@
1
1
  # @orkestrel/program
2
2
 
3
- A **program composition layer** over
4
- [`@orkestrel/qualifier`](https://github.com/orkestrel/qualifier) and
5
- [`@orkestrel/rater`](https://github.com/orkestrel/rater): a pure,
6
- JSON-serializable `ProgramDefinition` composes one qualification with an optional
7
- rating, plus optional notices, authority, and batch aggregate policy. `Program` executes
8
- the workflow in one direction qualify the subject, stop on terminal
9
- qualification, select eligible rating lines, rate only those lines, derive status,
10
- then evaluate optional authority returning a nested `ProgramResult` (or
11
- `AggregateResult` for batch `execute`). Globally ineligible or referred subjects
12
- never reach the rater. Execution never mutates its inputs every result is a
13
- fresh object. Environment-agnostic — no I/O, no browser or server assumptions.
14
- Part of the `@orkestrel` line.
3
+ > The program composition layer: a pure, JSON-serializable `ProgramDefinition`
4
+ > that composes one qualification with an optional rating, plus notices,
5
+ > authority, and batch aggregate policy, and a `Program` that executes that
6
+ > definition in one direction qualify, select, rate, derive status, then decide.
7
+
8
+ Build a definition with the `buildProgramDefinition` function, compile it with
9
+ `createProgram`, and call `execute` with one subject for a `ProgramResult` or
10
+ with a subject array for an `AggregateResult`. Execution never mutates its
11
+ inputs; every result is a fresh object. Injected qualifier, rater, and reason
12
+ instances stay caller-owned, and a standalone program owns the engine it creates.
13
+ Built over [`@orkestrel/qualifier`](https://github.com/orkestrel/qualifier),
14
+ [`@orkestrel/rater`](https://github.com/orkestrel/rater), and the shared
15
+ [`@orkestrel/reason`](https://github.com/orkestrel/reason) engine.
16
+ Environment-agnostic — no I/O, no browser or server assumptions. Part of the
17
+ `@orkestrel` line.
15
18
 
16
19
  ## Install
17
20
 
@@ -5,9 +5,15 @@ 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
- /** Names the default definition validation policy for `createProgram` / `ProgramManager.add`. */
8
+ /**
9
+ * Names the default definition validation policy, `true`, for `createProgram` /
10
+ * `ProgramManager.add`.
11
+ */
9
12
  var DEFAULT_PROGRAM_VALIDATE = true;
10
- /** Lists every {@link Status} literal — the source the union and its guard derive from. */
13
+ /**
14
+ * Lists every {@link Status} literal in tally order — the source the union and its
15
+ * guard derive from.
16
+ */
11
17
  var STATUSES = Object.freeze([
12
18
  "ineligible",
13
19
  "referral",
@@ -21,19 +27,26 @@ var ELIGIBILITY_DECISIONS = Object.freeze({
21
27
  ineligible: "denied",
22
28
  referral: "submitted"
23
29
  });
24
- /** Names the reserved working-subject key a batch's aggregate projection is written under. */
30
+ /**
31
+ * Names the reserved working-subject key a batch's aggregate projection is written
32
+ * under, `'aggregate'`.
33
+ */
25
34
  var AGGREGATE_KEY = "aggregate";
26
- /** Names the reserved working-subject key the authority's outcome projection is written under. */
35
+ /**
36
+ * Names the reserved working-subject key the authority's outcome projection is
37
+ * written under, `'outcome'`.
38
+ */
27
39
  var OUTCOME_KEY = "outcome";
28
40
  //#endregion
29
41
  //#region src/core/errors.ts
30
42
  /**
31
- * Reports a coded programmer error thrown by the program layer.
43
+ * Reports a coded programmer error thrown by the program layer, carrying a
44
+ * machine-readable code and an optional context and cause.
32
45
  *
33
46
  * @remarks
34
47
  * `DUPLICATE` — a program id collision on `ProgramManager.add`, or a duplicate
35
- * authored rating-line or notice id. `MISSING` — an
36
- * authored notice or qualification ruling scope names no rating line.
48
+ * authored rating-line or notice id. `MISSING` — an authored notice or
49
+ * qualification ruling scope names no rating line.
37
50
  * `DEFINITION` — a program, qualification, rating, authority, or aggregate
38
51
  * policy failed validation. `MISMATCH` — an injected entity or a returned
39
52
  * reason result has the wrong contract. `RESERVED` — a subject already
@@ -453,7 +466,7 @@ function assertProgramSubject(subject) {
453
466
  * A scope names a rating-line id. A line survives when its scope is absent
454
467
  * (eligible by default), `eligible`, or a `condition` (which is not an
455
468
  * eligibility value and never appears here). A scoped `ineligible` or `referral`
456
- * removes the line BEFORE the rater is invoked — the excluded line is never
469
+ * removes the line before the rater is invoked — the excluded line is never
457
470
  * evaluated merely to discard its amount.
458
471
  *
459
472
  * @param lines - The program's authored rating lines
@@ -481,9 +494,9 @@ function selectProgramLines(lines, scopes) {
481
494
  * Explicit policy, not an opaque precedence reduce: global
482
495
  * ineligibility or referral is terminal; a scoped referral yields `referral`;
483
496
  * an applied `condition` or an applied scoped `restriction` (a line was
484
- * removed but others rated) is `conditional`. When the definition OMITS
497
+ * removed but others rated) is `conditional`. When the definition omits
485
498
  * `rating` the program is eligibility-only — status resolves to `conditional`
486
- * or `eligible` and is NEVER `unrated`. Otherwise a subject with no successful
499
+ * or `eligible` and is never `unrated`. Otherwise a subject with no successful
487
500
  * rating is `unrated`.
488
501
  *
489
502
  * @param definition - The authored program definition
@@ -634,8 +647,8 @@ function buildOutcomeProjection(result) {
634
647
  * qualification succeeded, rating (when it ran) succeeded, and authority (when it
635
648
  * ran) produced no errors — a valid ineligible or referral outcome still
636
649
  * succeeds. `trace` and `errors` accumulate the qualification's, every rated
637
- * line's worksheet trail, and the authority's. A `decision` is present ONLY when
638
- * an authority ran (`options.authority`), the execution SUCCEEDED (`success`),
650
+ * line's worksheet trail, and the authority's. A `decision` is present only when
651
+ * an authority ran (`options.authority`), the execution succeeded (`success`),
639
652
  * no `limit` determination applied, and status is not `unrated`.
640
653
  *
641
654
  * @param definition - The authored program definition
@@ -865,7 +878,7 @@ function validateProgramDefinition(definition, qualifier, engine) {
865
878
  * @remarks
866
879
  * The key is the resolved field coerced with `String` — `undefined` collapses
867
880
  * to the empty string, so a subject missing the field and a subject whose
868
- * field is literally `''` land in the SAME partition, and a numeric `1`
881
+ * field is literally `''` land in the same partition, and a numeric `1`
869
882
  * collides with the string `'1'`.
870
883
  *
871
884
  * @param subject - The subject to key
@@ -886,7 +899,7 @@ function formatGroupKey(subject, partition) {
886
899
  * Folds one subject's finite aggregate field values into a sums record.
887
900
  *
888
901
  * @remarks
889
- * Returns a FRESH record — `sums` is never mutated. Only finite numbers
902
+ * Returns a fresh record — `sums` is never mutated. Only finite numbers
890
903
  * contribute; a non-numeric or absent value contributes zero (never a
891
904
  * coercion). A {@link FieldPath} may be nested — `formatField` renders the
892
905
  * dot-joined key the returned record is keyed by.
@@ -976,7 +989,7 @@ function aggregateGroups(subjects, fields, partition) {
976
989
  *
977
990
  * @remarks
978
991
  * The projection carries the whole-batch `count` and `sums` plus the subject's
979
- * OWN partition, located by the same {@link formatGroupKey} key
992
+ * own partition, located by the same {@link formatGroupKey} key
980
993
  * {@link aggregateGroups} partitions under.
981
994
  *
982
995
  * @param subject - The subject to project for
@@ -1140,7 +1153,7 @@ function tallySubject(tallies, result, subject, fields) {
1140
1153
  * @remarks
1141
1154
  * `count` is the subject count, `trace` / `errors` accumulate every subject's
1142
1155
  * plus the batch aggregate-gate evaluation's (`options.gates`), and `success`
1143
- * requires every subject execution to succeed AND the gate evaluation to have
1156
+ * requires every subject execution to succeed and the gate evaluation to have
1144
1157
  * produced no errors. A fired aggregate gate contributes a `limit`
1145
1158
  * determination, never a technical failure (a non-logical gate result is a
1146
1159
  * caller-facing `MISMATCH` thrown by `Program` before this assembles).
@@ -1219,6 +1232,9 @@ function buildProgramDefinition(id, name, qualification, rating, input) {
1219
1232
  /**
1220
1233
  * Builds a fresh {@link Notice}.
1221
1234
  *
1235
+ * @remarks
1236
+ * An absent `scope` is omitted entirely rather than stored as `undefined`.
1237
+ *
1222
1238
  * @param id - The notice id
1223
1239
  * @param message - The message template, carrying optional `{{token}}` placeholders
1224
1240
  * @param input - Optional presentation scope
@@ -1241,6 +1257,10 @@ function buildNotice(id, message, input) {
1241
1257
  /**
1242
1258
  * Builds a fresh {@link AggregateDefinition}.
1243
1259
  *
1260
+ * @remarks
1261
+ * `fields` is copied into a fresh array; an absent `partition` or `gates` is
1262
+ * omitted entirely rather than stored as `undefined`.
1263
+ *
1244
1264
  * @param fields - The aggregate fields to sum across a batch
1245
1265
  * @param input - Optional partition field and aggregate gates
1246
1266
  * @returns A fresh aggregate definition
@@ -1262,15 +1282,15 @@ function buildAggregateDefinition(fields, input) {
1262
1282
  //#endregion
1263
1283
  //#region src/core/programs/Program.ts
1264
1284
  /**
1265
- * Composes one qualifier and one rater over a shared reason engine and executes
1266
- * single subjects or aggregate-aware batches.
1285
+ * Composes one qualifier and one rater over a shared reason engine, compiling one
1286
+ * authored definition and executing single subjects or aggregate-aware batches.
1267
1287
  *
1268
1288
  * @remarks
1269
1289
  * Qualification decides whether rating happens: a globally ineligible, referred,
1270
1290
  * or failed subject never reaches the rater, and a scoped ineligibility removes
1271
1291
  * only its line before the first rating call. The rater always receives the
1272
- * ORIGINAL subject; the qualifier's aggregate projection stays private. When no
1273
- * qualifier, rater, or engine is injected the program creates ONE shared
1292
+ * original subject; the qualifier's aggregate projection stays private. When no
1293
+ * qualifier, rater, or engine is injected the program creates one shared
1274
1294
  * quantitative-plus-logical engine, injects it into the qualifier and rater it
1275
1295
  * creates, and destroys only what it owns. A definition failure during
1276
1296
  * construction (an invalid definition under `options.validate`) tears down
@@ -1280,8 +1300,8 @@ function buildAggregateDefinition(fields, input) {
1280
1300
  * or `Date` reached through a reason `Check.value` is cloned but remains mutable
1281
1301
  * because its contents live in internal slots. Uncloneable values and non-empty
1282
1302
  * typed arrays are refused with `ProgramError('DEFINITION')` and the host error
1283
- * as its cause. `destroy()` is idempotent and REENTRANCY-SAFE — the destroyed
1284
- * flag is set BEFORE any teardown or the `destroy` event fires, so a listener
1303
+ * as its cause. `destroy()` is idempotent and reentrancy-safe — the destroyed
1304
+ * flag is set before any teardown or the `destroy` event fires, so a listener
1285
1305
  * that re-enters `destroy()` is a no-op — and tears the emitter down last.
1286
1306
  */
1287
1307
  var Program = class {
@@ -1409,7 +1429,7 @@ var Program = class {
1409
1429
  * Destroys this program, idempotently.
1410
1430
  *
1411
1431
  * @remarks
1412
- * The destroyed flag is set BEFORE any teardown or the `destroy` event, so a
1432
+ * The destroyed flag is set before any teardown or the `destroy` event, so a
1413
1433
  * listener re-entering `destroy` is a no-op. An owned qualifier, rater, and reason
1414
1434
  * engine are destroyed; an injected one stays caller-owned. The emitter is torn
1415
1435
  * down last, and stays reachable afterwards.
@@ -1518,14 +1538,14 @@ var Program = class {
1518
1538
  * qualifier, rater, and reason engine across every program it compiles.
1519
1539
  *
1520
1540
  * @remarks
1521
- * OWNS its ordered `#programs` collection and its own {@link Emitter} over
1541
+ * owns its ordered `#programs` collection and its own {@link Emitter} over
1522
1542
  * {@link ProgramManagerEventMap}. Creates or borrows one shared engine, qualifier,
1523
1543
  * and rater and injects the same instances into every compiled program. `remove`
1524
1544
  * destroys the programs it removes; `destroy()` removes all programs, then
1525
- * destroys only the owned shared dependencies, and tears the emitter down LAST.
1545
+ * destroys only the owned shared dependencies, and tears the emitter down last.
1526
1546
  * A seed-program failure during construction tears the manager down (destroying
1527
1547
  * whatever had already been compiled) before rethrowing the original error.
1528
- * `destroy()` is REENTRANCY-SAFE — the destroyed flag is set BEFORE any teardown
1548
+ * `destroy()` is reentrancy-safe — the destroyed flag is set before any teardown
1529
1549
  * or the `remove` / `destroy` events fire, so a `remove` listener that re-enters
1530
1550
  * `destroy()` is a no-op. Every call after `destroy()` throws {@link ProgramError}
1531
1551
  * `'DESTROYED'`.
@@ -1735,7 +1755,7 @@ var ProgramManager = class {
1735
1755
  * Destroys this manager, idempotently.
1736
1756
  *
1737
1757
  * @remarks
1738
- * The destroyed flag is set BEFORE any teardown or the `remove` and `destroy`
1758
+ * The destroyed flag is set before any teardown or the `remove` and `destroy`
1739
1759
  * events, so a `remove` listener re-entering `destroy` is a no-op. Compiled
1740
1760
  * programs are destroyed first, then an owned qualifier, rater, and reason engine;
1741
1761
  * an injected one stays caller-owned. The emitter is torn down last, and stays
@@ -1782,34 +1802,39 @@ var ProgramManager = class {
1782
1802
  //#endregion
1783
1803
  //#region src/core/factories.ts
1784
1804
  /**
1785
- * Creates one compiled program over a qualifier and rater.
1805
+ * Creates one compiled {@link ProgramInterface} over a qualifier and rater.
1786
1806
  *
1787
1807
  * @remarks
1788
1808
  * If `options.validate` is `true`, the program validates the definition at
1789
1809
  * construction; if `false`, it compiles the definition unvalidated. Default:
1790
- * {@link DEFAULT_PROGRAM_VALIDATE}. A standalone program creates and
1791
- * OWNS one shared quantitative-plus-logical reason engine and injects it into the
1792
- * qualifier and rater it creates; injected dependencies remain caller-owned.
1810
+ * {@link DEFAULT_PROGRAM_VALIDATE}. A standalone program creates and owns one
1811
+ * shared quantitative-plus-logical reason engine and injects it into the qualifier
1812
+ * and rater it creates; injected dependencies remain caller-owned.
1793
1813
  *
1794
1814
  * @param definition - The authored program definition
1795
1815
  * @param options - Optional injected qualifier, rater, engine, validation, labels, and emitter hooks
1796
1816
  * @returns A {@link ProgramInterface}
1797
1817
  *
1798
- * @example
1818
+ * @example Compile a program and a manager
1799
1819
  * ```ts
1800
- * import { buildProgramDefinition, createProgram } from '@orkestrel/program'
1820
+ * import { buildProgramDefinition, createProgram, createProgramManager } from '@orkestrel/program'
1801
1821
  *
1802
1822
  * const definition = buildProgramDefinition('standard', 'Standard', qualification, rating)
1823
+ *
1803
1824
  * const program = createProgram(definition)
1825
+ * const manager = createProgramManager({ programs: [definition] })
1826
+ *
1804
1827
  * program.execute({ id: 'risk-1' })
1828
+ *
1805
1829
  * program.destroy()
1830
+ * manager.destroy()
1806
1831
  * ```
1807
1832
  */
1808
1833
  function createProgram(definition, options) {
1809
1834
  return new Program(definition, options);
1810
1835
  }
1811
1836
  /**
1812
- * Creates one ordered manager over compiled programs.
1837
+ * Creates one ordered {@link ProgramManagerInterface} over compiled programs.
1813
1838
  *
1814
1839
  * @remarks
1815
1840
  * Creates or borrows one shared reason engine, qualifier, and rater and injects