@orkestrel/reason 0.0.1
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/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/src/core/index.cjs +6087 -0
- package/dist/src/core/index.cjs.map +1 -0
- package/dist/src/core/index.d.cts +4852 -0
- package/dist/src/core/index.d.ts +4852 -0
- package/dist/src/core/index.js +5912 -0
- package/dist/src/core/index.js.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,4852 @@
|
|
|
1
|
+
import { EmitterErrorHandler } from '@orkestrel/emitter';
|
|
2
|
+
import { EmitterHooks } from '@orkestrel/emitter';
|
|
3
|
+
import { EmitterInterface } from '@orkestrel/emitter';
|
|
4
|
+
import { FieldPath } from '@orkestrel/contract';
|
|
5
|
+
import { Guard } from '@orkestrel/contract';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Upsert one entry of a {@link SymbolicDefinition}'s `variables`.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* `variables` is a name-keyed unordered record, so `add`/`remove` (no
|
|
12
|
+
* placement) are the correct verbs — mirrored by {@link removeVariable}.
|
|
13
|
+
*
|
|
14
|
+
* @param definition - The definition to update
|
|
15
|
+
* @param name - The variable name
|
|
16
|
+
* @param value - The variable's value
|
|
17
|
+
* @returns A fresh definition with the variable set
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { addVariable, symbolicDefinition } from '@src/core'
|
|
22
|
+
*
|
|
23
|
+
* addVariable(symbolicDefinition('e', 'E', []), 'x', 5).variables // { x: 5 }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function addVariable(definition: SymbolicDefinition, name: string, value: number): SymbolicDefinition;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* How the {@link AggregatorInterface} reduces a list of numbers to one.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* When weights apply: `sum` multiplies each value by its weight, `product`
|
|
33
|
+
* raises each value to its weight (weight-as-exponent), `average` is the
|
|
34
|
+
* weighted mean, and `minimum` / `maximum` ignore weights entirely.
|
|
35
|
+
*/
|
|
36
|
+
export declare type Aggregation = 'sum' | 'product' | 'average' | 'minimum' | 'maximum';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Reduces number lists to one number per {@link Aggregation} — the quantitative
|
|
40
|
+
* reasoner's group and definition combiner.
|
|
41
|
+
*
|
|
42
|
+
* @remarks
|
|
43
|
+
* TOTAL: never throws. Empty-input identities: `sum` / `average` → `0`,
|
|
44
|
+
* `product` → `1`, `minimum` / `maximum` → `NaN` (the deliberate "no data"
|
|
45
|
+
* signal). Weights are honored ONLY when their length matches `values` exactly
|
|
46
|
+
* (otherwise silently unweighted): a weight multiplies into a `sum`, acts as an
|
|
47
|
+
* EXPONENT for a `product`, is the weight of a weighted mean for `average`
|
|
48
|
+
* (a zero total weight yields `0`, never a division blow-up), and is ignored by
|
|
49
|
+
* `minimum` / `maximum`. An unknown aggregation yields `0`. Stateless and
|
|
50
|
+
* deterministic.
|
|
51
|
+
*/
|
|
52
|
+
export declare class Aggregator implements AggregatorInterface {
|
|
53
|
+
#private;
|
|
54
|
+
constructor(options?: AggregatorOptions);
|
|
55
|
+
get id(): string;
|
|
56
|
+
aggregate(values: readonly number[], aggregation: Aggregation, weights?: readonly number[]): number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Default `id` for an `Aggregator`. */
|
|
60
|
+
export declare const AGGREGATOR_ID = "aggregator";
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Reduces number lists to one number per {@link Aggregation}.
|
|
64
|
+
*
|
|
65
|
+
* @remarks
|
|
66
|
+
* Total: never throws. Empty-input identities: `sum` / `average` → `0`,
|
|
67
|
+
* `product` → `1`, `minimum` / `maximum` → `NaN`. `weights` are honored ONLY
|
|
68
|
+
* when their length matches `values` exactly (otherwise silently unweighted).
|
|
69
|
+
*/
|
|
70
|
+
export declare interface AggregatorInterface {
|
|
71
|
+
readonly id: string;
|
|
72
|
+
aggregate(values: readonly number[], aggregation: Aggregation, weights?: readonly number[]): number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Options for `createAggregator` / the `Aggregator` constructor.
|
|
77
|
+
*
|
|
78
|
+
* @remarks
|
|
79
|
+
* `id` — the aggregator's identity string (defaults to `AGGREGATOR_ID`).
|
|
80
|
+
*/
|
|
81
|
+
export declare interface AggregatorOptions {
|
|
82
|
+
readonly id?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Insert `item` into an id-keyed collection, deduping any existing element
|
|
87
|
+
* sharing its id, then placing it at the END (or immediately AFTER `target`).
|
|
88
|
+
*
|
|
89
|
+
* @remarks
|
|
90
|
+
* `filtered` is `items` with every `item.id` twin removed FIRST (dedup-on-
|
|
91
|
+
* insert — input arrays may already carry same-id twins per PROPOSAL.md §7).
|
|
92
|
+
* Re-appending an existing id therefore REPOSITIONS it rather than updating it
|
|
93
|
+
* in place — {@link replaceById} is the position-preserving alternative. With
|
|
94
|
+
* no `target`, `item` lands at the end; with a `target`, it lands immediately
|
|
95
|
+
* after the element whose `id === target` (searched in the DEDUPED array). A
|
|
96
|
+
* `target` naming no element throws {@link ReasonError} (`'TARGET'`).
|
|
97
|
+
*
|
|
98
|
+
* @typeParam T - An id-carrying element type
|
|
99
|
+
* @param items - The collection to insert into
|
|
100
|
+
* @param item - The element to insert
|
|
101
|
+
* @param target - Optional id to insert immediately after; appends at the end when absent
|
|
102
|
+
* @returns A fresh array with `item` inserted
|
|
103
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no element in `items`
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* import { appendById } from '@src/core'
|
|
108
|
+
*
|
|
109
|
+
* appendById([{ id: 'a' }, { id: 'b' }], { id: 'c' }) // [a, b, c]
|
|
110
|
+
* appendById([{ id: 'a' }, { id: 'b' }], { id: 'c' }, 'a') // [a, c, b]
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export declare function appendById<T extends {
|
|
114
|
+
readonly id: string;
|
|
115
|
+
}>(items: readonly T[], item: T, target?: string): readonly T[];
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Insert `equation` into a {@link SymbolicDefinition}'s `equations` — dedup-
|
|
119
|
+
* then-insert at the end, or immediately after `target`.
|
|
120
|
+
*
|
|
121
|
+
* @remarks
|
|
122
|
+
* Order is STRONGLY load-bearing: equations solve strictly in order and each
|
|
123
|
+
* rounded solution feeds forward.
|
|
124
|
+
*
|
|
125
|
+
* @param definition - The definition to insert into
|
|
126
|
+
* @param source - The equation to insert
|
|
127
|
+
* @param target - Optional equation id to insert immediately after
|
|
128
|
+
* @returns A fresh definition with `equation` inserted
|
|
129
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing equation
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* import { appendEquation, constant, equation, symbolicDefinition, variable } from '@src/core'
|
|
134
|
+
*
|
|
135
|
+
* appendEquation(symbolicDefinition('e', 'E', []), equation('e1', variable('x'), constant(1), 'x'))
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export declare function appendEquation(definition: SymbolicDefinition, source: Equation, target?: string): SymbolicDefinition;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Insert `fact` into an {@link InferentialDefinition}'s `facts` — dedup-then-
|
|
142
|
+
* insert at the end, or immediately after `target`.
|
|
143
|
+
*
|
|
144
|
+
* @remarks
|
|
145
|
+
* `Fact.id` is an AUTHORING label — the runtime content-dedups facts by
|
|
146
|
+
* predicate+arity+terms ({@link factToKey}), independently of this helper's
|
|
147
|
+
* id-keyed dedup.
|
|
148
|
+
*
|
|
149
|
+
* @param definition - The definition to insert into
|
|
150
|
+
* @param source - The fact to insert
|
|
151
|
+
* @param target - Optional fact id to insert immediately after
|
|
152
|
+
* @returns A fresh definition with `fact` inserted
|
|
153
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing fact
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* import { appendFact, fact, inferentialDefinition } from '@src/core'
|
|
158
|
+
*
|
|
159
|
+
* appendFact(inferentialDefinition('m', 'M', [], []), fact('f1', 'human', ['socrates']))
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export declare function appendFact(definition: InferentialDefinition, source: Fact, target?: string): InferentialDefinition;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Insert `factor` into a {@link FactorGroup}'s `factors` — dedup-then-insert
|
|
166
|
+
* at the end, or immediately after `target`.
|
|
167
|
+
*
|
|
168
|
+
* @remarks
|
|
169
|
+
* Factor order is LOAD-BEARING: the same-priority tiebreak is declaration
|
|
170
|
+
* order ({@link sortByPriority} is a stable ascending sort). Operates on the
|
|
171
|
+
* factor's DIRECT container — compose into a definition via
|
|
172
|
+
* `appendGroup(def, appendFactor(group, factor))`.
|
|
173
|
+
*
|
|
174
|
+
* @param group - The group to insert into
|
|
175
|
+
* @param factor - The factor to insert
|
|
176
|
+
* @param target - Optional factor id to insert immediately after
|
|
177
|
+
* @returns A fresh group with `factor` inserted
|
|
178
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing factor
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* import { appendFactor, factorGroup, staticFactor } from '@src/core'
|
|
183
|
+
*
|
|
184
|
+
* appendFactor(factorGroup('g1', 'sum', []), staticFactor('f1', 10))
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export declare function appendFactor(group: FactorGroup, factor: Factor, target?: string): FactorGroup;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Insert `group` into a {@link QuantitativeDefinition}'s `groups` — dedup-then-
|
|
191
|
+
* insert at the end, or immediately after `target`.
|
|
192
|
+
*
|
|
193
|
+
* @remarks
|
|
194
|
+
* Group order is COSMETIC (group aggregation is order-independent) but honored
|
|
195
|
+
* uniformly, same as every `append*` helper. Composes with {@link appendFactor}:
|
|
196
|
+
* `appendGroup(def, appendFactor(group, factor))`.
|
|
197
|
+
*
|
|
198
|
+
* @param definition - The definition to insert into
|
|
199
|
+
* @param group - The group to insert
|
|
200
|
+
* @param target - Optional group id to insert immediately after
|
|
201
|
+
* @returns A fresh definition with `group` inserted
|
|
202
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing group
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* import { appendGroup, factorGroup, quantitativeDefinition } from '@src/core'
|
|
207
|
+
*
|
|
208
|
+
* appendGroup(quantitativeDefinition('risk', 'Risk', []), factorGroup('g1', 'sum', []))
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
export declare function appendGroup(definition: QuantitativeDefinition, group: FactorGroup, target?: string): QuantitativeDefinition;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Insert `inference` into an {@link InferentialDefinition}'s `inferences` —
|
|
215
|
+
* dedup-then-insert at the end, or immediately after `target`.
|
|
216
|
+
*
|
|
217
|
+
* @remarks
|
|
218
|
+
* Order is LOAD-BEARING: backward proving iterates in declaration order and
|
|
219
|
+
* returns on first success.
|
|
220
|
+
*
|
|
221
|
+
* @param definition - The definition to insert into
|
|
222
|
+
* @param source - The inference to insert
|
|
223
|
+
* @param target - Optional inference id to insert immediately after
|
|
224
|
+
* @returns A fresh definition with `inference` inserted
|
|
225
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing inference
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```ts
|
|
229
|
+
* import { appendInference, fact, inference, inferentialDefinition } from '@src/core'
|
|
230
|
+
*
|
|
231
|
+
* appendInference(
|
|
232
|
+
* inferentialDefinition('m', 'M', [], []),
|
|
233
|
+
* inference('i1', [fact('p', 'human', ['?x'])], fact('c', 'mortal', ['?x'])),
|
|
234
|
+
* )
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
export declare function appendInference(definition: InferentialDefinition, source: Inference, target?: string): InferentialDefinition;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Insert `rule` into a {@link LogicalDefinition}'s `rules` — dedup-then-insert
|
|
241
|
+
* at the end, or immediately after `target`.
|
|
242
|
+
*
|
|
243
|
+
* @remarks
|
|
244
|
+
* Order is LOAD-BEARING: the forward conclusion is the LAST declared
|
|
245
|
+
* non-disabled rule, so `appendRule` without a `target` makes the new rule the
|
|
246
|
+
* conclusion.
|
|
247
|
+
*
|
|
248
|
+
* @param definition - The definition to insert into
|
|
249
|
+
* @param source - The rule to insert
|
|
250
|
+
* @param target - Optional rule id to insert immediately after
|
|
251
|
+
* @returns A fresh definition with `rule` inserted
|
|
252
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing rule
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```ts
|
|
256
|
+
* import { appendRule, atom, logicalDefinition, rule } from '@src/core'
|
|
257
|
+
*
|
|
258
|
+
* appendRule(logicalDefinition('e', 'E', []), rule('r1', [], atom('a', 'equals', true)))
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
export declare function appendRule(definition: LogicalDefinition, source: Rule, target?: string): LogicalDefinition;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Apply one binary/unary math operation to already-evaluated operands.
|
|
265
|
+
*
|
|
266
|
+
* @remarks
|
|
267
|
+
* The arithmetic core of the symbolic reasoner's expression evaluation: the full
|
|
268
|
+
* {@link MathOperation} vocabulary plus its zero / unary conventions — `divide`
|
|
269
|
+
* by zero is `NaN` (never a throw), the unary operations (`round` / `ceil` /
|
|
270
|
+
* `floor` / `abs`) ignore `right`, and `percentage` is `left * (right / 100)`.
|
|
271
|
+
* `operator` is typed `string` because untrusted definitions reach here
|
|
272
|
+
* unchecked; the ONE throwing path is the unknown-operator default (caught per
|
|
273
|
+
* equation upstream).
|
|
274
|
+
*
|
|
275
|
+
* @param operator - The operation name (untrusted — an unknown one throws)
|
|
276
|
+
* @param left - The left operand
|
|
277
|
+
* @param right - The right operand (ignored by the unary operations)
|
|
278
|
+
* @returns The operation's result (`NaN` on divide-by-zero)
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```ts
|
|
282
|
+
* import { applyOperation } from '@src/core'
|
|
283
|
+
*
|
|
284
|
+
* applyOperation('add', 2, 3) // 5
|
|
285
|
+
* applyOperation('divide', 1, 0) // NaN
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
export declare function applyOperation(operator: string, left: number, right: number): number;
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Upsert one field of a {@link Subject} — copy-on-write spread.
|
|
292
|
+
*
|
|
293
|
+
* @remarks
|
|
294
|
+
* Id-agnostic: overwrites an `id` key like any other field — id protection is
|
|
295
|
+
* an entity's job, not this helper's.
|
|
296
|
+
*
|
|
297
|
+
* @param subject - The subject to update
|
|
298
|
+
* @param key - The field to set
|
|
299
|
+
* @param value - The value to set it to
|
|
300
|
+
* @returns A fresh subject with `key` set to `value`
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* ```ts
|
|
304
|
+
* import { assignField } from '@src/core'
|
|
305
|
+
*
|
|
306
|
+
* assignField({ id: 's1', age: 30 }, 'age', 31) // { id: 's1', age: 31 }
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
309
|
+
export declare function assignField(subject: Subject, key: string, value: unknown): Subject;
|
|
310
|
+
|
|
311
|
+
/** A leaf boolean expression — one {@link Check} against the subject. */
|
|
312
|
+
export declare interface Atom {
|
|
313
|
+
readonly form: 'atom';
|
|
314
|
+
readonly check: Check;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Build an atom {@link Expression} — a leaf wrapping one {@link Check}.
|
|
319
|
+
*
|
|
320
|
+
* @param field - The subject field to resolve
|
|
321
|
+
* @param operator - The comparison to apply
|
|
322
|
+
* @param value - The expected value
|
|
323
|
+
* @returns A fresh atom expression
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```ts
|
|
327
|
+
* import { atom } from '@src/core'
|
|
328
|
+
*
|
|
329
|
+
* atom('age', 'from', 18) // { form: 'atom', check: { field: 'age', operator: 'from', value: 18 } }
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
export declare function atom(field: FieldPath, operator: Comparison, value: unknown): Expression;
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* An inclusive numeric clamp — either side may be absent (unbounded).
|
|
336
|
+
*/
|
|
337
|
+
export declare interface Bounds {
|
|
338
|
+
readonly minimum?: number;
|
|
339
|
+
readonly maximum?: number;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Build a {@link Bounds} — an inclusive numeric clamp.
|
|
344
|
+
*
|
|
345
|
+
* @remarks
|
|
346
|
+
* Absent sides are OMITTED (never set to `undefined`) — an absent bound is
|
|
347
|
+
* unbounded on that side.
|
|
348
|
+
*
|
|
349
|
+
* @param minimum - The inclusive lower bound
|
|
350
|
+
* @param maximum - The inclusive upper bound
|
|
351
|
+
* @returns A fresh bounds record
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```ts
|
|
355
|
+
* import { bounds } from '@src/core'
|
|
356
|
+
*
|
|
357
|
+
* bounds(0, 100) // { minimum: 0, maximum: 100 }
|
|
358
|
+
* bounds(undefined, 100) // { maximum: 100 }
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
export declare function bounds(minimum?: number, maximum?: number): Bounds;
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Build the empty, type-shaped failure {@link ReasonResult} matching a
|
|
365
|
+
* definition's reasoning.
|
|
366
|
+
*
|
|
367
|
+
* @remarks
|
|
368
|
+
* The `bail: false` fallback of the `Reason` orchestrator: when a reasoner
|
|
369
|
+
* throws and bail is off, the throw becomes an empty failure result carrying the
|
|
370
|
+
* message as its sole `errors` entry. Each reasoning gets its own zero-valued
|
|
371
|
+
* shape (`quantitative` → `value: 0` / empty `groups`; `logical` → `conclusion:
|
|
372
|
+
* false` / empty `rules`; `symbolic` → empty `solutions`; `inferential` → empty
|
|
373
|
+
* `derived`), always with `success: false` and an empty `trace`.
|
|
374
|
+
*
|
|
375
|
+
* @param definition - The definition whose reasoning selects the result shape
|
|
376
|
+
* @param message - The error message to carry as the result's sole `errors` entry
|
|
377
|
+
* @returns A fresh failure result of the matching reasoning
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* ```ts
|
|
381
|
+
* import { buildErrorResult, logicalDefinition } from '@src/core'
|
|
382
|
+
*
|
|
383
|
+
* const result = buildErrorResult(logicalDefinition('e', 'E', []), 'boom')
|
|
384
|
+
* // { reasoning: 'logical', conclusion: false, rules: [], count: 0, success: false, trace: [], errors: ['boom'] }
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
export declare function buildErrorResult(definition: Definition, message: string): ReasonResult;
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* How a chaining reasoner walks its rules: `forward` (data-driven fixpoint) or
|
|
391
|
+
* `backward` (goal-driven proving).
|
|
392
|
+
*/
|
|
393
|
+
export declare type ChainingStrategy = 'forward' | 'backward';
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* A single field predicate: resolve `field` from the subject and compare it to
|
|
397
|
+
* `value` with `operator`.
|
|
398
|
+
*
|
|
399
|
+
* @remarks
|
|
400
|
+
* `field` follows the {@link FieldPath} idiom — a string is ONE key (never
|
|
401
|
+
* dot-split), an array descends into nested objects. `value` is unconstrained
|
|
402
|
+
* (`unknown`): the operator decides what shapes are meaningful.
|
|
403
|
+
*/
|
|
404
|
+
export declare interface Check {
|
|
405
|
+
readonly field: FieldPath;
|
|
406
|
+
readonly operator: Comparison;
|
|
407
|
+
readonly value: unknown;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Build a {@link Check} — one field predicate.
|
|
412
|
+
*
|
|
413
|
+
* @param field - The subject field to resolve (a string is ONE key; an array descends)
|
|
414
|
+
* @param operator - The comparison to apply
|
|
415
|
+
* @param value - The expected value (any type — the operator decides what is meaningful)
|
|
416
|
+
* @returns A fresh check
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* ```ts
|
|
420
|
+
* import { check } from '@src/core'
|
|
421
|
+
*
|
|
422
|
+
* check('age', 'from', 18) // { field: 'age', operator: 'from', value: 18 }
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
export declare function check(field: FieldPath, operator: Comparison, value: unknown): Check;
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* The outcome of one {@link Check} evaluation.
|
|
429
|
+
*
|
|
430
|
+
* @remarks
|
|
431
|
+
* `actual` is the resolved subject value (possibly `undefined`); `error` is set
|
|
432
|
+
* ONLY when evaluation itself failed (an unknown operator) — a merely-unmet
|
|
433
|
+
* check carries no `error`.
|
|
434
|
+
*/
|
|
435
|
+
export declare interface CheckResult {
|
|
436
|
+
readonly field: FieldPath;
|
|
437
|
+
readonly met: boolean;
|
|
438
|
+
readonly actual: unknown;
|
|
439
|
+
readonly error?: string;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Clamp a number to inclusive {@link Bounds}.
|
|
444
|
+
*
|
|
445
|
+
* @remarks
|
|
446
|
+
* An absent bound (or absent `bounds` entirely) never constrains that side.
|
|
447
|
+
* `NaN` flows through unchanged (every comparison with `NaN` is false).
|
|
448
|
+
*
|
|
449
|
+
* @param value - The number to clamp
|
|
450
|
+
* @param limit - The inclusive bounds (either side optional)
|
|
451
|
+
* @returns The clamped number
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* ```ts
|
|
455
|
+
* import { clamp } from '@src/core'
|
|
456
|
+
*
|
|
457
|
+
* clamp(150, { minimum: 0, maximum: 100 }) // 100
|
|
458
|
+
* clamp(150) // 150 — unbounded
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
export declare function clamp(value: number, limit?: Bounds): number;
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Delete one optional field of an {@link InferentialDefinition}.
|
|
465
|
+
*
|
|
466
|
+
* @param definition - The definition to update
|
|
467
|
+
* @param key - The optional field to clear
|
|
468
|
+
* @returns A fresh definition with `key` omitted
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* ```ts
|
|
472
|
+
* import { clearInferentialDefinition, inferentialDefinition } from '@src/core'
|
|
473
|
+
*
|
|
474
|
+
* const definition = inferentialDefinition('m', 'M', [], [], { depth: 5 })
|
|
475
|
+
* 'depth' in clearInferentialDefinition(definition, 'depth') // false
|
|
476
|
+
* ```
|
|
477
|
+
*/
|
|
478
|
+
export declare function clearInferentialDefinition(definition: InferentialDefinition, key: 'description' | 'depth'): InferentialDefinition;
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Delete one optional field of a {@link LogicalDefinition}.
|
|
482
|
+
*
|
|
483
|
+
* @param definition - The definition to update
|
|
484
|
+
* @param key - The optional field to clear
|
|
485
|
+
* @returns A fresh definition with `key` omitted
|
|
486
|
+
*
|
|
487
|
+
* @example
|
|
488
|
+
* ```ts
|
|
489
|
+
* import { clearLogicalDefinition, logicalDefinition } from '@src/core'
|
|
490
|
+
*
|
|
491
|
+
* const definition = logicalDefinition('e', 'E', [], { depth: 5 })
|
|
492
|
+
* 'depth' in clearLogicalDefinition(definition, 'depth') // false
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
495
|
+
export declare function clearLogicalDefinition(definition: LogicalDefinition, key: 'description' | 'depth'): LogicalDefinition;
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Delete one optional field of a {@link QuantitativeDefinition}.
|
|
499
|
+
*
|
|
500
|
+
* @param definition - The definition to update
|
|
501
|
+
* @param key - The optional field to clear
|
|
502
|
+
* @returns A fresh definition with `key` omitted
|
|
503
|
+
*
|
|
504
|
+
* @example
|
|
505
|
+
* ```ts
|
|
506
|
+
* import { clearQuantitativeDefinition, quantitativeDefinition } from '@src/core'
|
|
507
|
+
*
|
|
508
|
+
* const definition = quantitativeDefinition('risk', 'Risk', [], { precision: 2 })
|
|
509
|
+
* 'precision' in clearQuantitativeDefinition(definition, 'precision') // false
|
|
510
|
+
* ```
|
|
511
|
+
*/
|
|
512
|
+
export declare function clearQuantitativeDefinition(definition: QuantitativeDefinition, key: 'description' | 'base' | 'bounds' | 'precision'): QuantitativeDefinition;
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Delete one optional field of a {@link SymbolicDefinition}.
|
|
516
|
+
*
|
|
517
|
+
* @param definition - The definition to update
|
|
518
|
+
* @param key - The optional field to clear
|
|
519
|
+
* @returns A fresh definition with `key` omitted
|
|
520
|
+
*
|
|
521
|
+
* @example
|
|
522
|
+
* ```ts
|
|
523
|
+
* import { clearSymbolicDefinition, symbolicDefinition } from '@src/core'
|
|
524
|
+
*
|
|
525
|
+
* const definition = symbolicDefinition('e', 'E', [], { precision: 2 })
|
|
526
|
+
* 'precision' in clearSymbolicDefinition(definition, 'precision') // false
|
|
527
|
+
* ```
|
|
528
|
+
*/
|
|
529
|
+
export declare function clearSymbolicDefinition(definition: SymbolicDefinition, key: 'description' | 'precision'): SymbolicDefinition;
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* The comparison a {@link Check} applies between a resolved subject field and
|
|
533
|
+
* its expected value.
|
|
534
|
+
*
|
|
535
|
+
* @remarks
|
|
536
|
+
* `equals` / `not` — strict `===` / `!==` (no coercion). `above` / `below` /
|
|
537
|
+
* `from` / `to` — `>` / `<` / `>=` / `<=`, requiring numbers on BOTH sides
|
|
538
|
+
* (anything else is not met). `any` / `none` — array membership / non-membership
|
|
539
|
+
* (a non-array expected value is not met for BOTH — `none` is not the raw
|
|
540
|
+
* complement of `any` on malformed input). `between` / `outside` — inclusive
|
|
541
|
+
* range test on the first two numeric elements of the expected array; `outside`
|
|
542
|
+
* IS the pure negation of `between`, so a malformed range is `outside`.
|
|
543
|
+
*/
|
|
544
|
+
export declare type Comparison = 'equals' | 'not' | 'above' | 'below' | 'from' | 'to' | 'any' | 'none' | 'between' | 'outside';
|
|
545
|
+
|
|
546
|
+
/** A compound boolean expression — a {@link LogicalOperator} over nested operands. */
|
|
547
|
+
export declare interface Compound {
|
|
548
|
+
readonly form: 'compound';
|
|
549
|
+
readonly operator: LogicalOperator;
|
|
550
|
+
readonly operands: readonly Expression[];
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Build a compound {@link Expression} — a logical connective over nested
|
|
555
|
+
* operands.
|
|
556
|
+
*
|
|
557
|
+
* @param operator - The logical connective
|
|
558
|
+
* @param operands - The nested expressions it combines
|
|
559
|
+
* @returns A fresh compound expression
|
|
560
|
+
*
|
|
561
|
+
* @example
|
|
562
|
+
* ```ts
|
|
563
|
+
* import { atom, compound } from '@src/core'
|
|
564
|
+
*
|
|
565
|
+
* compound('and', [atom('age', 'from', 18), atom('state', 'equals', 'CA')])
|
|
566
|
+
* ```
|
|
567
|
+
*/
|
|
568
|
+
export declare function compound(operator: LogicalOperator, operands: readonly Expression[]): Expression;
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Decimal places a derived fact's confidence is rounded to during forward
|
|
572
|
+
* inferential chaining.
|
|
573
|
+
*
|
|
574
|
+
* @remarks
|
|
575
|
+
* Fixed — unlike {@link DEFAULT_PRECISION} it is NOT overridable per
|
|
576
|
+
* definition, so confidence products stay comparable across derivations.
|
|
577
|
+
*/
|
|
578
|
+
export declare const CONFIDENCE_PRECISION = 4;
|
|
579
|
+
|
|
580
|
+
/** A symbolic expression leaf holding a fixed number. */
|
|
581
|
+
export declare interface Constant {
|
|
582
|
+
readonly form: 'constant';
|
|
583
|
+
readonly value: number;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Build a constant {@link SymbolicExpression} leaf.
|
|
588
|
+
*
|
|
589
|
+
* @param value - The fixed number
|
|
590
|
+
* @returns A fresh constant node
|
|
591
|
+
*
|
|
592
|
+
* @example
|
|
593
|
+
* ```ts
|
|
594
|
+
* import { constant } from '@src/core'
|
|
595
|
+
*
|
|
596
|
+
* constant(42) // { form: 'constant', value: 42 }
|
|
597
|
+
* ```
|
|
598
|
+
*/
|
|
599
|
+
export declare function constant(value: number): SymbolicExpression;
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Determine whether a symbolic expression contains an UNBOUND occurrence of a
|
|
603
|
+
* target variable.
|
|
604
|
+
*
|
|
605
|
+
* @remarks
|
|
606
|
+
* The variable-presence probe of the symbolic reasoner's isolation: a `variable`
|
|
607
|
+
* node matches only when its name is `target` AND `target` is not already in
|
|
608
|
+
* `bindings` (a pre-bound target is a known value, not an unknown to isolate); a
|
|
609
|
+
* `constant` never matches; an `operation` recurses into both operands (the
|
|
610
|
+
* `right` operand may be absent on a unary node). The walk is an ITERATIVE
|
|
611
|
+
* worklist (never recursive) with short-circuit `true` on the first hit, so it
|
|
612
|
+
* stays total on pathologically deep expression trees.
|
|
613
|
+
*
|
|
614
|
+
* @param expression - The expression to probe
|
|
615
|
+
* @param target - The variable name being sought
|
|
616
|
+
* @param bindings - The known bindings (a bound target does NOT count as present)
|
|
617
|
+
* @returns `true` when an unbound `target` occurs in the expression
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* ```ts
|
|
621
|
+
* import { containsVariable, operation, variable, constant } from '@src/core'
|
|
622
|
+
*
|
|
623
|
+
* containsVariable(operation('add', variable('x'), constant(1)), 'x', {}) // true
|
|
624
|
+
* containsVariable(operation('add', variable('x'), constant(1)), 'x', { x: 5 }) // false — pre-bound
|
|
625
|
+
* ```
|
|
626
|
+
*/
|
|
627
|
+
export declare function containsVariable(expression: SymbolicExpression, target: string, bindings: Record<string, number>): boolean;
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Create a number aggregator.
|
|
631
|
+
*
|
|
632
|
+
* @remarks
|
|
633
|
+
* `id` — the aggregator's identity string (defaults to `'aggregator'`).
|
|
634
|
+
*
|
|
635
|
+
* @param options - Optional `id`
|
|
636
|
+
* @returns A stateless {@link AggregatorInterface}
|
|
637
|
+
*
|
|
638
|
+
* @example
|
|
639
|
+
* ```ts
|
|
640
|
+
* import { createAggregator } from '@src/core'
|
|
641
|
+
*
|
|
642
|
+
* const aggregator = createAggregator()
|
|
643
|
+
* aggregator.aggregate([10, 20], 'average', [1, 3]) // 17.5 — weighted mean
|
|
644
|
+
* ```
|
|
645
|
+
*/
|
|
646
|
+
export declare function createAggregator(options?: AggregatorOptions): AggregatorInterface;
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Create a `DefinitionBuilder` — a stateful workspace builder accumulating a
|
|
650
|
+
* {@link Definition} through seven self-owning manager properties.
|
|
651
|
+
*
|
|
652
|
+
* @remarks
|
|
653
|
+
* `id` defaults to `seed.id`. Each manager slot is BRING-YOUR-OWN (a supplied
|
|
654
|
+
* one is reused, else a fresh one is seeded from the seed's matching
|
|
655
|
+
* collection). `on` — initial event listeners (AGENTS §8). `error` — the
|
|
656
|
+
* emitter's listener-error handler (AGENTS §13). Mutate through the manager
|
|
657
|
+
* properties (`groups` / `factors` / `rules` / `equations` / `variables` /
|
|
658
|
+
* `facts` / `inferences`) and `merge` / `clear`, then call `build()` to produce
|
|
659
|
+
* a fresh, plain {@link Definition} snapshot.
|
|
660
|
+
*
|
|
661
|
+
* @param seed - The starting definition (any of the four reasoning kinds)
|
|
662
|
+
* @param options - Optional `id` override, manager injections, and emitter hooks
|
|
663
|
+
* @returns A {@link DefinitionBuilderInterface}
|
|
664
|
+
*
|
|
665
|
+
* @example
|
|
666
|
+
* ```ts
|
|
667
|
+
* import { createDefinitionBuilder, quantitativeDefinition } from '@src/core'
|
|
668
|
+
*
|
|
669
|
+
* const definition = createDefinitionBuilder(quantitativeDefinition('risk', 'Risk', []))
|
|
670
|
+
* definition.groups.append({ id: 'g1', name: 'g1', aggregation: 'sum', factors: [] })
|
|
671
|
+
* definition.build() // a fresh QuantitativeDefinition with the group applied
|
|
672
|
+
* definition.destroy()
|
|
673
|
+
* ```
|
|
674
|
+
*/
|
|
675
|
+
export declare function createDefinitionBuilder(seed: Definition, options?: DefinitionBuilderOptions): DefinitionBuilderInterface;
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Create an `EquationManager` — a self-owning manager over a symbolic
|
|
679
|
+
* definition's `equations`.
|
|
680
|
+
*
|
|
681
|
+
* @remarks
|
|
682
|
+
* `equations` — the initial collection (defaults to empty). `on` / `error` —
|
|
683
|
+
* emitter hooks (AGENTS §8 / §13). Equation order is strongly load-bearing.
|
|
684
|
+
*
|
|
685
|
+
* @param options - Optional seed collection and emitter hooks
|
|
686
|
+
* @returns An {@link EquationManagerInterface}
|
|
687
|
+
*
|
|
688
|
+
* @example
|
|
689
|
+
* ```ts
|
|
690
|
+
* import { constant, createEquationManager, equation, variable } from '@src/core'
|
|
691
|
+
*
|
|
692
|
+
* const equations = createEquationManager()
|
|
693
|
+
* equations.append(equation('e1', variable('y'), constant(2), 'y'))
|
|
694
|
+
* ```
|
|
695
|
+
*/
|
|
696
|
+
export declare function createEquationManager(options?: EquationManagerOptions): EquationManagerInterface;
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Create a check evaluator.
|
|
700
|
+
*
|
|
701
|
+
* @remarks
|
|
702
|
+
* `id` — the evaluator's identity string (defaults to `'evaluator'`).
|
|
703
|
+
*
|
|
704
|
+
* @param options - Optional `id`
|
|
705
|
+
* @returns A stateless {@link EvaluatorInterface}
|
|
706
|
+
*
|
|
707
|
+
* @example
|
|
708
|
+
* ```ts
|
|
709
|
+
* import { createEvaluator } from '@src/core'
|
|
710
|
+
*
|
|
711
|
+
* const evaluator = createEvaluator()
|
|
712
|
+
* evaluator.evaluate({ field: 'age', operator: 'above', value: 18 }, { age: 25 })
|
|
713
|
+
* // { field: 'age', met: true, actual: 25 }
|
|
714
|
+
* ```
|
|
715
|
+
*/
|
|
716
|
+
export declare function createEvaluator(options?: EvaluatorOptions): EvaluatorInterface;
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Create a `FactManager` — a self-owning manager over an inferential
|
|
720
|
+
* definition's `facts`.
|
|
721
|
+
*
|
|
722
|
+
* @remarks
|
|
723
|
+
* `facts` — the initial collection (defaults to empty). `on` / `error` —
|
|
724
|
+
* emitter hooks (AGENTS §8 / §13).
|
|
725
|
+
*
|
|
726
|
+
* @param options - Optional seed collection and emitter hooks
|
|
727
|
+
* @returns A {@link FactManagerInterface}
|
|
728
|
+
*
|
|
729
|
+
* @example
|
|
730
|
+
* ```ts
|
|
731
|
+
* import { createFactManager, fact } from '@src/core'
|
|
732
|
+
*
|
|
733
|
+
* const facts = createFactManager()
|
|
734
|
+
* facts.append(fact('f1', 'human', ['socrates']))
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
export declare function createFactManager(options?: FactManagerOptions): FactManagerInterface;
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Create a `FactorManager` — the divergent manager over a group's `factors`,
|
|
741
|
+
* threaded through a required `groupId` locator.
|
|
742
|
+
*
|
|
743
|
+
* @remarks
|
|
744
|
+
* Holds no collection state of its own — it reads and writes factors through
|
|
745
|
+
* the injected sibling {@link GroupManagerInterface}. `on` / `error` — emitter
|
|
746
|
+
* hooks (AGENTS §8 / §13).
|
|
747
|
+
*
|
|
748
|
+
* @param groups - The sibling group manager factors are located within
|
|
749
|
+
* @param options - Optional emitter hooks
|
|
750
|
+
* @returns A {@link FactorManagerInterface}
|
|
751
|
+
*
|
|
752
|
+
* @example
|
|
753
|
+
* ```ts
|
|
754
|
+
* import { createFactorManager, createGroupManager, factorGroup, staticFactor } from '@src/core'
|
|
755
|
+
*
|
|
756
|
+
* const groups = createGroupManager({ groups: [factorGroup('g1', 'sum', [])] })
|
|
757
|
+
* const factors = createFactorManager(groups)
|
|
758
|
+
* factors.append('g1', staticFactor('f1', 10))
|
|
759
|
+
* ```
|
|
760
|
+
*/
|
|
761
|
+
export declare function createFactorManager(groups: GroupManagerInterface, options?: FactorManagerOptions): FactorManagerInterface;
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Create a `GroupManager` — a self-owning manager over a quantitative
|
|
765
|
+
* definition's `groups`.
|
|
766
|
+
*
|
|
767
|
+
* @remarks
|
|
768
|
+
* `groups` — the initial collection (defaults to empty). `on` / `error` —
|
|
769
|
+
* emitter hooks (AGENTS §8 / §13). Kind-free: hand it to a
|
|
770
|
+
* {@link createDefinitionBuilder} `groups` slot regardless of reasoning.
|
|
771
|
+
*
|
|
772
|
+
* @param options - Optional seed collection and emitter hooks
|
|
773
|
+
* @returns A {@link GroupManagerInterface}
|
|
774
|
+
*
|
|
775
|
+
* @example
|
|
776
|
+
* ```ts
|
|
777
|
+
* import { createGroupManager, factorGroup } from '@src/core'
|
|
778
|
+
*
|
|
779
|
+
* const groups = createGroupManager({ groups: [factorGroup('g1', 'sum', [])] })
|
|
780
|
+
* groups.append(factorGroup('g2', 'sum', []))
|
|
781
|
+
* ```
|
|
782
|
+
*/
|
|
783
|
+
export declare function createGroupManager(options?: GroupManagerOptions): GroupManagerInterface;
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Create an `InferenceManager` — a self-owning manager over an inferential
|
|
787
|
+
* definition's `inferences`.
|
|
788
|
+
*
|
|
789
|
+
* @remarks
|
|
790
|
+
* `inferences` — the initial collection (defaults to empty). `on` / `error` —
|
|
791
|
+
* emitter hooks (AGENTS §8 / §13). Inference order is load-bearing.
|
|
792
|
+
*
|
|
793
|
+
* @param options - Optional seed collection and emitter hooks
|
|
794
|
+
* @returns An {@link InferenceManagerInterface}
|
|
795
|
+
*
|
|
796
|
+
* @example
|
|
797
|
+
* ```ts
|
|
798
|
+
* import { createInferenceManager, fact, inference } from '@src/core'
|
|
799
|
+
*
|
|
800
|
+
* const inferences = createInferenceManager()
|
|
801
|
+
* inferences.append(inference('m', [fact('p', 'human', ['?x'])], fact('c', 'mortal', ['?x'])))
|
|
802
|
+
* ```
|
|
803
|
+
*/
|
|
804
|
+
export declare function createInferenceManager(options?: InferenceManagerOptions): InferenceManagerInterface;
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Create the inferential reasoner — fact derivation with unification variables
|
|
808
|
+
* and proof trees.
|
|
809
|
+
*
|
|
810
|
+
* @remarks
|
|
811
|
+
* `id` — the reasoner's identity string (defaults to `'inferential'`).
|
|
812
|
+
*
|
|
813
|
+
* @param options - Optional `id`
|
|
814
|
+
* @returns A {@link ReasonerInterface} with reasoning `'inferential'`
|
|
815
|
+
*
|
|
816
|
+
* @example
|
|
817
|
+
* ```ts
|
|
818
|
+
* import { createInferentialReasoner, fact, inference, inferentialDefinition } from '@src/core'
|
|
819
|
+
*
|
|
820
|
+
* const reasoner = createInferentialReasoner()
|
|
821
|
+
* const definition = inferentialDefinition('mortality', 'Mortality',
|
|
822
|
+
* [fact('f1', 'human', ['socrates'])],
|
|
823
|
+
* [inference('mortal', [fact('p1', 'human', ['?x'])], fact('c1', 'mortal', ['?x']))],
|
|
824
|
+
* )
|
|
825
|
+
* reasoner.reason({}, definition) // derives mortal(socrates)
|
|
826
|
+
* ```
|
|
827
|
+
*/
|
|
828
|
+
export declare function createInferentialReasoner(options?: InferentialReasonerOptions): ReasonerInterface;
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Create the logical reasoner — rule-based deduction with forward / backward
|
|
832
|
+
* chaining.
|
|
833
|
+
*
|
|
834
|
+
* @remarks
|
|
835
|
+
* `id` — the reasoner's identity string (defaults to `'logical'`). `evaluator`
|
|
836
|
+
* — the injectable check evaluator (defaults to a fresh instance).
|
|
837
|
+
*
|
|
838
|
+
* @param options - Optional `id` and evaluator injection
|
|
839
|
+
* @returns A {@link ReasonerInterface} with reasoning `'logical'`
|
|
840
|
+
*
|
|
841
|
+
* @example
|
|
842
|
+
* ```ts
|
|
843
|
+
* import { atom, createLogicalReasoner, logicalDefinition, rule } from '@src/core'
|
|
844
|
+
*
|
|
845
|
+
* const reasoner = createLogicalReasoner()
|
|
846
|
+
* const definition = logicalDefinition('eligibility', 'Eligibility', [
|
|
847
|
+
* rule('adult', [atom('age', 'from', 18)], atom('adult', 'equals', true)),
|
|
848
|
+
* ])
|
|
849
|
+
* reasoner.reason({ age: 25 }, definition) // conclusion true
|
|
850
|
+
* ```
|
|
851
|
+
*/
|
|
852
|
+
export declare function createLogicalReasoner(options?: LogicalReasonerOptions): ReasonerInterface;
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Create the quantitative reasoner — factor-based numeric scoring.
|
|
856
|
+
*
|
|
857
|
+
* @remarks
|
|
858
|
+
* `id` — the reasoner's identity string (defaults to `'quantitative'`).
|
|
859
|
+
* `evaluator` / `transformer` / `aggregator` — injectable operators, each
|
|
860
|
+
* defaulting to a fresh default-constructed instance.
|
|
861
|
+
*
|
|
862
|
+
* @param options - Optional `id` and operator injections
|
|
863
|
+
* @returns A {@link ReasonerInterface} with reasoning `'quantitative'`
|
|
864
|
+
*
|
|
865
|
+
* @example
|
|
866
|
+
* ```ts
|
|
867
|
+
* import { createQuantitativeReasoner, fieldFactor, factorGroup, quantitativeDefinition } from '@src/core'
|
|
868
|
+
*
|
|
869
|
+
* const reasoner = createQuantitativeReasoner()
|
|
870
|
+
* const definition = quantitativeDefinition('risk', 'Risk', [
|
|
871
|
+
* factorGroup('g1', 'sum', [fieldFactor('age', 'age')]),
|
|
872
|
+
* ], { base: 100 })
|
|
873
|
+
* reasoner.reason({ age: 25 }, definition) // value 125
|
|
874
|
+
* ```
|
|
875
|
+
*/
|
|
876
|
+
export declare function createQuantitativeReasoner(options?: QuantitativeReasonerOptions): ReasonerInterface;
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Create the reasoning orchestrator.
|
|
880
|
+
*
|
|
881
|
+
* @remarks
|
|
882
|
+
* `reasoners` — the initial registry (a later entry of the same reasoning
|
|
883
|
+
* replaces an earlier one; the orchestrator ships with NO defaults). `bail` —
|
|
884
|
+
* `true` (the default) rethrows a reasoner throw after the `error` emit;
|
|
885
|
+
* `false` converts it to a failure result. `validate` — validate every
|
|
886
|
+
* definition before running it, throwing `INVALID` on failure (default
|
|
887
|
+
* `false`). `on` — initial event listeners (AGENTS §8). `error` — the
|
|
888
|
+
* emitter's listener-error handler (AGENTS §13).
|
|
889
|
+
*
|
|
890
|
+
* @param options - Optional registry, policies, and emitter hooks
|
|
891
|
+
* @returns A {@link ReasonInterface}
|
|
892
|
+
*
|
|
893
|
+
* @example
|
|
894
|
+
* ```ts
|
|
895
|
+
* import { createLogicalReasoner, createQuantitativeReasoner, createReason } from '@src/core'
|
|
896
|
+
*
|
|
897
|
+
* const reason = createReason({
|
|
898
|
+
* reasoners: [createQuantitativeReasoner(), createLogicalReasoner()],
|
|
899
|
+
* on: { reason: (result) => console.log(result.success) },
|
|
900
|
+
* })
|
|
901
|
+
* const result = reason.reason({ age: 25 }, definition)
|
|
902
|
+
* reason.destroy()
|
|
903
|
+
* ```
|
|
904
|
+
*/
|
|
905
|
+
export declare function createReason(options?: ReasonOptions): ReasonInterface;
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Create a `RuleManager` — a self-owning manager over a logical definition's
|
|
909
|
+
* `rules`.
|
|
910
|
+
*
|
|
911
|
+
* @remarks
|
|
912
|
+
* `rules` — the initial collection (defaults to empty). `on` / `error` —
|
|
913
|
+
* emitter hooks (AGENTS §8 / §13). Rule order is load-bearing.
|
|
914
|
+
*
|
|
915
|
+
* @param options - Optional seed collection and emitter hooks
|
|
916
|
+
* @returns A {@link RuleManagerInterface}
|
|
917
|
+
*
|
|
918
|
+
* @example
|
|
919
|
+
* ```ts
|
|
920
|
+
* import { atom, createRuleManager, rule } from '@src/core'
|
|
921
|
+
*
|
|
922
|
+
* const rules = createRuleManager()
|
|
923
|
+
* rules.append(rule('adult', [atom('age', 'from', 18)], atom('adult', 'equals', true)))
|
|
924
|
+
* ```
|
|
925
|
+
*/
|
|
926
|
+
export declare function createRuleManager(options?: RuleManagerOptions): RuleManagerInterface;
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Create a `SubjectBuilder` — a stateful workspace builder accumulating a
|
|
930
|
+
* {@link Subject}.
|
|
931
|
+
*
|
|
932
|
+
* @remarks
|
|
933
|
+
* `id` defaults to `seed.id` and is OPTIONAL — when neither `options.id` nor
|
|
934
|
+
* a string `seed.id` is present the builder is ANONYMOUS (`.id` is
|
|
935
|
+
* `undefined`, `build()` emits no `id` key). `on` — initial event listeners
|
|
936
|
+
* (AGENTS §8). `error` — the emitter's listener-error handler (AGENTS §13).
|
|
937
|
+
* Mutate through `set` / `remove` / `merge` / `clear`, then call `build()` to
|
|
938
|
+
* produce a fresh, plain {@link Subject} snapshot.
|
|
939
|
+
*
|
|
940
|
+
* @param seed - The starting subject
|
|
941
|
+
* @param options - Optional `id` override and emitter hooks
|
|
942
|
+
* @returns A {@link SubjectBuilderInterface}
|
|
943
|
+
*
|
|
944
|
+
* @example
|
|
945
|
+
* ```ts
|
|
946
|
+
* import { createSubjectBuilder } from '@src/core'
|
|
947
|
+
*
|
|
948
|
+
* const subject = createSubjectBuilder({ id: 's1', age: 30 })
|
|
949
|
+
* subject.set('age', 31)
|
|
950
|
+
* subject.build() // { id: 's1', age: 31 }
|
|
951
|
+
* subject.destroy()
|
|
952
|
+
* ```
|
|
953
|
+
*/
|
|
954
|
+
export declare function createSubjectBuilder(seed: Subject, options?: SubjectBuilderOptions): SubjectBuilderInterface;
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Create the symbolic reasoner — algebraic equation solving by variable
|
|
958
|
+
* isolation.
|
|
959
|
+
*
|
|
960
|
+
* @remarks
|
|
961
|
+
* `id` — the reasoner's identity string (defaults to `'symbolic'`).
|
|
962
|
+
*
|
|
963
|
+
* @param options - Optional `id`
|
|
964
|
+
* @returns A {@link ReasonerInterface} with reasoning `'symbolic'`
|
|
965
|
+
*
|
|
966
|
+
* @example
|
|
967
|
+
* ```ts
|
|
968
|
+
* import { constant, createSymbolicReasoner, equation, operation, symbolicDefinition, variable } from '@src/core'
|
|
969
|
+
*
|
|
970
|
+
* const reasoner = createSymbolicReasoner()
|
|
971
|
+
* const definition = symbolicDefinition('double', 'Double', [
|
|
972
|
+
* equation('e1', variable('y'), operation('multiply', variable('x'), constant(2)), 'y'),
|
|
973
|
+
* ])
|
|
974
|
+
* reasoner.reason({ x: 21 }, definition) // solutions.y === 42
|
|
975
|
+
* ```
|
|
976
|
+
*/
|
|
977
|
+
export declare function createSymbolicReasoner(options?: SymbolicReasonerOptions): ReasonerInterface;
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Create a math transformer.
|
|
981
|
+
*
|
|
982
|
+
* @remarks
|
|
983
|
+
* `id` — the transformer's identity string (defaults to `'transformer'`).
|
|
984
|
+
*
|
|
985
|
+
* @param options - Optional `id`
|
|
986
|
+
* @returns A stateless {@link TransformerInterface}
|
|
987
|
+
*
|
|
988
|
+
* @example
|
|
989
|
+
* ```ts
|
|
990
|
+
* import { createTransformer, transform } from '@src/core'
|
|
991
|
+
*
|
|
992
|
+
* const transformer = createTransformer()
|
|
993
|
+
* transformer.chain(100, [transform('add', 50), transform('multiply', 2)]) // 300
|
|
994
|
+
* ```
|
|
995
|
+
*/
|
|
996
|
+
export declare function createTransformer(options?: TransformerOptions): TransformerInterface;
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
* Create a `VariableManager` — a self-owning manager over a symbolic
|
|
1000
|
+
* definition's `variables` (a name-keyed record; `add` / `remove` only).
|
|
1001
|
+
*
|
|
1002
|
+
* @remarks
|
|
1003
|
+
* `variables` — the initial record (defaults to empty). `on` / `error` —
|
|
1004
|
+
* emitter hooks (AGENTS §8 / §13).
|
|
1005
|
+
*
|
|
1006
|
+
* @param options - Optional seed record and emitter hooks
|
|
1007
|
+
* @returns A {@link VariableManagerInterface}
|
|
1008
|
+
*
|
|
1009
|
+
* @example
|
|
1010
|
+
* ```ts
|
|
1011
|
+
* import { createVariableManager } from '@src/core'
|
|
1012
|
+
*
|
|
1013
|
+
* const variables = createVariableManager({ variables: { x: 1 } })
|
|
1014
|
+
* variables.add('y', 2)
|
|
1015
|
+
* ```
|
|
1016
|
+
*/
|
|
1017
|
+
export declare function createVariableManager(options?: VariableManagerOptions): VariableManagerInterface;
|
|
1018
|
+
|
|
1019
|
+
/** Default `base` added before aggregation, at both group and definition level. */
|
|
1020
|
+
export declare const DEFAULT_BASE = 0;
|
|
1021
|
+
|
|
1022
|
+
/** Default `confidence` for facts, inferences, and injected subject facts. */
|
|
1023
|
+
export declare const DEFAULT_CONFIDENCE = 1;
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Default `depth` for chaining definitions — the forward-iteration /
|
|
1027
|
+
* backward-recursion cap of the logical and inferential reasoners.
|
|
1028
|
+
*/
|
|
1029
|
+
export declare const DEFAULT_DEPTH = 10;
|
|
1030
|
+
|
|
1031
|
+
/** Default `precision` (decimal places) for quantitative values and symbolic solutions. */
|
|
1032
|
+
export declare const DEFAULT_PRECISION = 4;
|
|
1033
|
+
|
|
1034
|
+
/** Default factor / rule `priority` — evaluation order is ascending and stable. */
|
|
1035
|
+
export declare const DEFAULT_PRIORITY = 0;
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* Default `bail` for the `Reason` orchestrator — a reasoner throw is rethrown
|
|
1039
|
+
* after the `error` emit.
|
|
1040
|
+
*
|
|
1041
|
+
* @remarks
|
|
1042
|
+
* Named with the domain qualifier so the generic `DEFAULT_BAIL` stays free for
|
|
1043
|
+
* other modules' bail defaults on the shared `@src/core` barrel.
|
|
1044
|
+
*/
|
|
1045
|
+
export declare const DEFAULT_REASON_BAIL = true;
|
|
1046
|
+
|
|
1047
|
+
/** Default `validate` for the `Reason` orchestrator — per-call validation is skipped. */
|
|
1048
|
+
export declare const DEFAULT_VALIDATE = false;
|
|
1049
|
+
|
|
1050
|
+
/** Default factor `weight` at group aggregation. */
|
|
1051
|
+
export declare const DEFAULT_WEIGHT = 1;
|
|
1052
|
+
|
|
1053
|
+
/** Any reasoning definition, discriminated by `reasoning`. */
|
|
1054
|
+
export declare type Definition = QuantitativeDefinition | LogicalDefinition | SymbolicDefinition | InferentialDefinition;
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* The `DefinitionBuilder` entity brand — a `unique symbol` key carrying
|
|
1058
|
+
* `readonly true` on every `DefinitionBuilderInterface` instance.
|
|
1059
|
+
*
|
|
1060
|
+
* @remarks
|
|
1061
|
+
* Only `isDefinitionBuilder` (`validators.ts`) reads this key, via
|
|
1062
|
+
* `Reflect.get`. A module-owned `unique symbol` cannot be produced by
|
|
1063
|
+
* `JSON.parse` or written by any consumer that does not import this constant,
|
|
1064
|
+
* so plain data can never forge the brand.
|
|
1065
|
+
*/
|
|
1066
|
+
export declare const DEFINITION_BUILDER_BRAND: unique symbol;
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* A stateful workspace builder accumulating a {@link Definition} through seven
|
|
1070
|
+
* always-present self-owning manager properties, shaped like `AgentContext`
|
|
1071
|
+
* (AGENTS §4.2.2): a private SCALAR ENVELOPE (reasoning / id / name plus the
|
|
1072
|
+
* kind's scalars) composed with each collection read from its manager.
|
|
1073
|
+
*
|
|
1074
|
+
* @remarks
|
|
1075
|
+
* Each manager is BRING-YOUR-OWN (a supplied one is reused) or a fresh one
|
|
1076
|
+
* seeded from the seed's matching collection (empty for off-kind collections).
|
|
1077
|
+
* Managers are KIND-FREE — an off-kind manager is simply ignored by `build()`,
|
|
1078
|
+
* never a `MISMATCH`. `build()` is TOTAL, deterministic, and returns a FRESH
|
|
1079
|
+
* plain {@link Definition} each call. `merge(incoming)` requires the SAME
|
|
1080
|
+
* `reasoning` (else `MISMATCH`) and distributes incoming scalars into the
|
|
1081
|
+
* envelope and collections into the managers via the matching `merge*` helper.
|
|
1082
|
+
* `clear(key)` deletes one optional field of the envelope for the instance's
|
|
1083
|
+
* `reasoning` (a non-clearable key throws `MISMATCH`). `destroy()` cascades to
|
|
1084
|
+
* all seven managers, emits `destroy`, then tears the builder emitter down LAST
|
|
1085
|
+
* (AGENTS §13); it is idempotent, and post-destroy mutation / build throws
|
|
1086
|
+
* `ReasonError('DESTROYED', …)`.
|
|
1087
|
+
*/
|
|
1088
|
+
export declare class DefinitionBuilder implements DefinitionBuilderInterface {
|
|
1089
|
+
#private;
|
|
1090
|
+
readonly [DEFINITION_BUILDER_BRAND]: true;
|
|
1091
|
+
readonly groups: GroupManagerInterface;
|
|
1092
|
+
readonly factors: FactorManagerInterface;
|
|
1093
|
+
readonly rules: RuleManagerInterface;
|
|
1094
|
+
readonly equations: EquationManagerInterface;
|
|
1095
|
+
readonly variables: VariableManagerInterface;
|
|
1096
|
+
readonly facts: FactManagerInterface;
|
|
1097
|
+
readonly inferences: InferenceManagerInterface;
|
|
1098
|
+
constructor(seed: Definition, options?: DefinitionBuilderOptions);
|
|
1099
|
+
get id(): string;
|
|
1100
|
+
get reasoning(): Reasoning;
|
|
1101
|
+
get emitter(): EmitterInterface<DefinitionBuilderEventMap>;
|
|
1102
|
+
build(): Definition;
|
|
1103
|
+
merge(incoming: Definition): void;
|
|
1104
|
+
clear(key: string): void;
|
|
1105
|
+
destroy(): void;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* The push observation surface of a {@link DefinitionBuilderInterface}
|
|
1110
|
+
* (AGENTS §13) — the builder-level lifecycle events; per-element mutation
|
|
1111
|
+
* events live on the individual managers' own emitters.
|
|
1112
|
+
*
|
|
1113
|
+
* @remarks
|
|
1114
|
+
* `merge` carries the definition's `reasoning`; `clear` carries the cleared
|
|
1115
|
+
* key; `destroy` fires once on teardown.
|
|
1116
|
+
*/
|
|
1117
|
+
export declare type DefinitionBuilderEventMap = {
|
|
1118
|
+
/** The definition was reconciled with an incoming definition — carries the reasoning. */
|
|
1119
|
+
readonly merge: readonly [reasoning: Reasoning];
|
|
1120
|
+
/** An optional field was cleared — carries the field key. */
|
|
1121
|
+
readonly clear: readonly [key: string];
|
|
1122
|
+
/** The entity was destroyed. */
|
|
1123
|
+
readonly destroy: readonly [];
|
|
1124
|
+
};
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* A stateful workspace builder accumulating a {@link Definition} through seven
|
|
1128
|
+
* always-present self-owning manager properties, taverna `AgentContext`-shaped
|
|
1129
|
+
* (AGENTS §4.2.2): a private scalar envelope plus one manager per collection.
|
|
1130
|
+
*
|
|
1131
|
+
* @remarks
|
|
1132
|
+
* `build()` is TOTAL, deterministic, and returns a FRESH plain
|
|
1133
|
+
* {@link Definition} each call (the scalar envelope composed with the kind's
|
|
1134
|
+
* collections, read from the relevant managers' plural accessors — off-kind
|
|
1135
|
+
* managers are ignored). `merge(incoming)` requires the SAME `reasoning`,
|
|
1136
|
+
* distributes incoming scalars into the envelope and collections into the
|
|
1137
|
+
* managers via the matching `merge*` helper (a cross-reasoning `incoming`
|
|
1138
|
+
* throws `ReasonError('MISMATCH', …)`). `clear(key)` is the uniform
|
|
1139
|
+
* optional-key selector (AGENTS §4.2.4) over the scalar envelope; a `key` that
|
|
1140
|
+
* is not a clearable optional field for the current kind throws
|
|
1141
|
+
* `ReasonError('MISMATCH', …, { key, reasoning })`. `destroy()` cascades to all
|
|
1142
|
+
* seven managers, emits `destroy`, then tears the builder emitter down LAST;
|
|
1143
|
+
* it is idempotent, and post-destroy mutation / build throws
|
|
1144
|
+
* `ReasonError('DESTROYED', …)` — only the `emitter` / manager getters and
|
|
1145
|
+
* `destroy` keep working.
|
|
1146
|
+
*/
|
|
1147
|
+
export declare interface DefinitionBuilderInterface {
|
|
1148
|
+
readonly [DEFINITION_BUILDER_BRAND]: true;
|
|
1149
|
+
readonly id: string;
|
|
1150
|
+
readonly reasoning: Reasoning;
|
|
1151
|
+
readonly emitter: EmitterInterface<DefinitionBuilderEventMap>;
|
|
1152
|
+
readonly groups: GroupManagerInterface;
|
|
1153
|
+
readonly factors: FactorManagerInterface;
|
|
1154
|
+
readonly rules: RuleManagerInterface;
|
|
1155
|
+
readonly equations: EquationManagerInterface;
|
|
1156
|
+
readonly variables: VariableManagerInterface;
|
|
1157
|
+
readonly facts: FactManagerInterface;
|
|
1158
|
+
readonly inferences: InferenceManagerInterface;
|
|
1159
|
+
build(): Definition;
|
|
1160
|
+
merge(incoming: Definition): void;
|
|
1161
|
+
clear(key: string): void;
|
|
1162
|
+
destroy(): void;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
/**
|
|
1166
|
+
* Options for `createDefinitionBuilder` / the `DefinitionBuilder` constructor.
|
|
1167
|
+
*
|
|
1168
|
+
* @remarks
|
|
1169
|
+
* `id` — overrides the seed definition's `id` (defaults to `seed.id`). Each of
|
|
1170
|
+
* the seven manager slots is BRING-YOUR-OWN — a supplied manager is reused,
|
|
1171
|
+
* else one is constructed and seeded from the seed's matching collection. `on`
|
|
1172
|
+
* — initial event listeners (AGENTS §8). `error` — the emitter's
|
|
1173
|
+
* listener-error handler (AGENTS §13).
|
|
1174
|
+
*/
|
|
1175
|
+
export declare interface DefinitionBuilderOptions {
|
|
1176
|
+
readonly id?: string;
|
|
1177
|
+
readonly groups?: GroupManagerInterface;
|
|
1178
|
+
readonly factors?: FactorManagerInterface;
|
|
1179
|
+
readonly rules?: RuleManagerInterface;
|
|
1180
|
+
readonly equations?: EquationManagerInterface;
|
|
1181
|
+
readonly variables?: VariableManagerInterface;
|
|
1182
|
+
readonly facts?: FactManagerInterface;
|
|
1183
|
+
readonly inferences?: InferenceManagerInterface;
|
|
1184
|
+
readonly on?: EmitterHooks<DefinitionBuilderEventMap>;
|
|
1185
|
+
readonly error?: EmitterErrorHandler;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
/**
|
|
1189
|
+
* The scalar-only projection of each definition kind — the {@link DefinitionBuilderInterface}
|
|
1190
|
+
* implementation's private envelope holds the non-collection fields; `build()` re-composes
|
|
1191
|
+
* the kind's collections from the managers' plural accessors.
|
|
1192
|
+
*/
|
|
1193
|
+
export declare type DefinitionEnvelope = Omit<QuantitativeDefinition, 'groups'> | Omit<LogicalDefinition, 'rules'> | Omit<SymbolicDefinition, 'equations' | 'variables'> | Omit<InferentialDefinition, 'facts' | 'inferences'>;
|
|
1194
|
+
|
|
1195
|
+
/**
|
|
1196
|
+
* Determine whether two values are SameValueZero-equal — strict `===` with
|
|
1197
|
+
* `NaN` equal to itself (and, unlike `Object.is`, `+0` equal to `-0`).
|
|
1198
|
+
*
|
|
1199
|
+
* @remarks
|
|
1200
|
+
* This is the derivation-bookkeeping equality of the chaining reasoners: the
|
|
1201
|
+
* logical overlay and the inferential fact-dedupe compare with it so a
|
|
1202
|
+
* NaN-valued conclusion or fact term derives exactly ONCE and the fixpoint
|
|
1203
|
+
* converges (raw `===` would re-derive it every iteration, never converging).
|
|
1204
|
+
* It matches `Array.prototype.includes` semantics — the same membership test
|
|
1205
|
+
* the `any` / `none` comparisons use.
|
|
1206
|
+
*
|
|
1207
|
+
* @param left - The first value
|
|
1208
|
+
* @param right - The second value
|
|
1209
|
+
* @returns `true` when the values are SameValueZero-equal
|
|
1210
|
+
*
|
|
1211
|
+
* @example
|
|
1212
|
+
* ```ts
|
|
1213
|
+
* import { equalValues } from '@src/core'
|
|
1214
|
+
*
|
|
1215
|
+
* equalValues(Number.NaN, Number.NaN) // true — unlike ===
|
|
1216
|
+
* equalValues(0, -0) // true — unlike Object.is
|
|
1217
|
+
* equalValues(1, '1') // false — no coercion
|
|
1218
|
+
* ```
|
|
1219
|
+
*/
|
|
1220
|
+
export declare function equalValues(left: unknown, right: unknown): boolean;
|
|
1221
|
+
|
|
1222
|
+
/**
|
|
1223
|
+
* One equation `left = right`, solved for the `target` variable.
|
|
1224
|
+
*
|
|
1225
|
+
* @remarks
|
|
1226
|
+
* When `target` is unbound and appears on exactly one side, it is isolated
|
|
1227
|
+
* algebraically through invertible operations (`add` / `subtract` / `multiply`
|
|
1228
|
+
* / `divide`); otherwise the right side is evaluated directly.
|
|
1229
|
+
*/
|
|
1230
|
+
export declare interface Equation {
|
|
1231
|
+
readonly id: string;
|
|
1232
|
+
readonly name: string;
|
|
1233
|
+
readonly description?: string;
|
|
1234
|
+
readonly left: SymbolicExpression;
|
|
1235
|
+
readonly right: SymbolicExpression;
|
|
1236
|
+
readonly target: string;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/**
|
|
1240
|
+
* Build an {@link Equation} — `left = right`, solved for `target`.
|
|
1241
|
+
*
|
|
1242
|
+
* @remarks
|
|
1243
|
+
* `name` defaults to the `id`; set `name` or `description` through `overrides`.
|
|
1244
|
+
*
|
|
1245
|
+
* @param id - The equation id
|
|
1246
|
+
* @param left - The left side
|
|
1247
|
+
* @param right - The right side
|
|
1248
|
+
* @param target - The variable name to solve for
|
|
1249
|
+
* @param overrides - Optional {@link Equation} fields merged over the defaults
|
|
1250
|
+
* @returns A fresh equation
|
|
1251
|
+
*
|
|
1252
|
+
* @example
|
|
1253
|
+
* ```ts
|
|
1254
|
+
* import { constant, equation, operation, variable } from '@src/core'
|
|
1255
|
+
*
|
|
1256
|
+
* // 2x + 3 = 11 — solved for x
|
|
1257
|
+
* equation('e1', operation('add', operation('multiply', constant(2), variable('x')), constant(3)), constant(11), 'x')
|
|
1258
|
+
* ```
|
|
1259
|
+
*/
|
|
1260
|
+
export declare function equation(id: string, left: SymbolicExpression, right: SymbolicExpression, target: string, overrides?: Partial<Omit<Equation, 'id' | 'left' | 'right' | 'target'>>): Equation;
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* The {@link EquationManagerInterface} implementation — a self-owning,
|
|
1264
|
+
* kind-free manager over a symbolic definition's `equations`.
|
|
1265
|
+
*
|
|
1266
|
+
* @remarks
|
|
1267
|
+
* OWNS its `#equations` collection as private copy-on-write state and its own
|
|
1268
|
+
* {@link Emitter} over {@link EquationManagerEventMap}. Equation order is
|
|
1269
|
+
* STRONGLY load-bearing — equations solve strictly in order and each rounded
|
|
1270
|
+
* solution feeds forward. The write-only `collection` setter is the owning
|
|
1271
|
+
* builder's silent bulk re-seat channel (used by `merge`). `destroy()` is
|
|
1272
|
+
* idempotent and tears the emitter down LAST; any other call after it throws
|
|
1273
|
+
* `ReasonError('DESTROYED', …)`.
|
|
1274
|
+
*/
|
|
1275
|
+
export declare class EquationManager implements EquationManagerInterface {
|
|
1276
|
+
#private;
|
|
1277
|
+
constructor(options?: EquationManagerOptions);
|
|
1278
|
+
get emitter(): EmitterInterface<EquationManagerEventMap>;
|
|
1279
|
+
set collection(value: readonly Equation[]);
|
|
1280
|
+
equation(id: string): Equation | undefined;
|
|
1281
|
+
equations(): readonly Equation[];
|
|
1282
|
+
append(equation: Equation, target?: string): void;
|
|
1283
|
+
prepend(equation: Equation, target?: string): void;
|
|
1284
|
+
replace(equation: Equation): void;
|
|
1285
|
+
remove(id: string): void;
|
|
1286
|
+
destroy(): void;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
/** The push observation surface of an {@link EquationManagerInterface} (AGENTS §13). */
|
|
1290
|
+
export declare type EquationManagerEventMap = {
|
|
1291
|
+
/** An equation was appended — carries its id. */
|
|
1292
|
+
readonly append: readonly [id: string];
|
|
1293
|
+
/** An equation was prepended — carries its id. */
|
|
1294
|
+
readonly prepend: readonly [id: string];
|
|
1295
|
+
/** An equation was replaced in place — carries its id. */
|
|
1296
|
+
readonly replace: readonly [id: string];
|
|
1297
|
+
/** An equation was removed — carries its id. */
|
|
1298
|
+
readonly remove: readonly [id: string];
|
|
1299
|
+
/** The manager was destroyed. */
|
|
1300
|
+
readonly destroy: readonly [];
|
|
1301
|
+
};
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* The {@link DefinitionBuilderInterface} manager over a symbolic definition's
|
|
1305
|
+
* `equations` — a self-owning, kind-free collection manager.
|
|
1306
|
+
*
|
|
1307
|
+
* @remarks
|
|
1308
|
+
* Equation order is strongly load-bearing — equations solve strictly in
|
|
1309
|
+
* order and each rounded solution feeds forward.
|
|
1310
|
+
*/
|
|
1311
|
+
export declare interface EquationManagerInterface {
|
|
1312
|
+
readonly emitter: EmitterInterface<EquationManagerEventMap>;
|
|
1313
|
+
set collection(value: readonly Equation[]);
|
|
1314
|
+
equation(id: string): Equation | undefined;
|
|
1315
|
+
equations(): readonly Equation[];
|
|
1316
|
+
append(equation: Equation, target?: string): void;
|
|
1317
|
+
prepend(equation: Equation, target?: string): void;
|
|
1318
|
+
replace(equation: Equation): void;
|
|
1319
|
+
remove(id: string): void;
|
|
1320
|
+
destroy(): void;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
/**
|
|
1324
|
+
* Options for `createEquationManager` / the `EquationManager` constructor.
|
|
1325
|
+
*
|
|
1326
|
+
* @remarks
|
|
1327
|
+
* `equations` — the initial collection (defaults to empty). `on` — initial
|
|
1328
|
+
* event listeners (AGENTS §8). `error` — the emitter's listener-error handler
|
|
1329
|
+
* (AGENTS §13).
|
|
1330
|
+
*/
|
|
1331
|
+
export declare interface EquationManagerOptions {
|
|
1332
|
+
readonly equations?: readonly Equation[];
|
|
1333
|
+
readonly on?: EmitterHooks<EquationManagerEventMap>;
|
|
1334
|
+
readonly error?: EmitterErrorHandler;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Evaluates {@link Check}s against subjects — the shared predicate engine of the
|
|
1339
|
+
* quantitative and logical reasoners.
|
|
1340
|
+
*
|
|
1341
|
+
* @remarks
|
|
1342
|
+
* TOTAL and strict: `evaluate` never throws (an unknown operator is caught and
|
|
1343
|
+
* surfaced as `CheckResult.error` with `met: false`) and never coerces —
|
|
1344
|
+
* `equals` / `not` are raw `===` / `!==`, the ordering operators demand numbers
|
|
1345
|
+
* on BOTH sides, `any` / `none` demand an array expected value (a non-array is
|
|
1346
|
+
* not met for either — `none` is NOT the raw complement of `any` on malformed
|
|
1347
|
+
* input), while `outside` IS the pure negation of `between` (so a malformed
|
|
1348
|
+
* range is `outside`). Fields resolve through the core `resolveField` — a string
|
|
1349
|
+
* is ONE key, an array descends. Stateless and deterministic.
|
|
1350
|
+
*/
|
|
1351
|
+
export declare class Evaluator implements EvaluatorInterface {
|
|
1352
|
+
#private;
|
|
1353
|
+
constructor(options?: EvaluatorOptions);
|
|
1354
|
+
get id(): string;
|
|
1355
|
+
evaluate(check: Check, subject: Subject): CheckResult;
|
|
1356
|
+
batch(checks: readonly Check[], subject: Subject): readonly CheckResult[];
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
/** Default `id` for an `Evaluator`. */
|
|
1360
|
+
export declare const EVALUATOR_ID = "evaluator";
|
|
1361
|
+
|
|
1362
|
+
/**
|
|
1363
|
+
* Evaluates {@link Check}s against subjects.
|
|
1364
|
+
*
|
|
1365
|
+
* @remarks
|
|
1366
|
+
* Total: `evaluate` never throws — an unknown operator surfaces as
|
|
1367
|
+
* `CheckResult.error` with `met: false`, and errors are isolated per item in
|
|
1368
|
+
* `batch`.
|
|
1369
|
+
*/
|
|
1370
|
+
export declare interface EvaluatorInterface {
|
|
1371
|
+
readonly id: string;
|
|
1372
|
+
evaluate(check: Check, subject: Subject): CheckResult;
|
|
1373
|
+
batch(checks: readonly Check[], subject: Subject): readonly CheckResult[];
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
/**
|
|
1377
|
+
* Options for `createEvaluator` / the `Evaluator` constructor.
|
|
1378
|
+
*
|
|
1379
|
+
* @remarks
|
|
1380
|
+
* `id` — the evaluator's identity string (defaults to `EVALUATOR_ID`).
|
|
1381
|
+
*/
|
|
1382
|
+
export declare interface EvaluatorOptions {
|
|
1383
|
+
readonly id?: string;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
/** A boolean expression tree, discriminated by `form`. */
|
|
1387
|
+
export declare type Expression = Atom | Compound;
|
|
1388
|
+
|
|
1389
|
+
/**
|
|
1390
|
+
* Return every atom leaf of an expression tree, depth-first, left-to-right.
|
|
1391
|
+
*
|
|
1392
|
+
* @remarks
|
|
1393
|
+
* The shared atom-walk behind both {@link extractConclusions} and the raters'
|
|
1394
|
+
* conclusion merge: an `atom` yields itself; a compound flattens its operands in
|
|
1395
|
+
* authored order, so a later operand's atoms follow an earlier one's. The walk is
|
|
1396
|
+
* an ITERATIVE explicit-stack traversal (never recursive), so it stays total on
|
|
1397
|
+
* pathologically deep expression trees; a hole in an `operands` array is skipped,
|
|
1398
|
+
* matching `flatMap`'s hole-skipping behavior.
|
|
1399
|
+
*
|
|
1400
|
+
* @param expression - The expression tree to walk
|
|
1401
|
+
* @returns A fresh, ordered list of the atom leaves
|
|
1402
|
+
*
|
|
1403
|
+
* @example
|
|
1404
|
+
* ```ts
|
|
1405
|
+
* import { atom, compound, extractAtoms } from '@src/core'
|
|
1406
|
+
*
|
|
1407
|
+
* extractAtoms(atom('a', 'equals', 1)).length // 1
|
|
1408
|
+
* extractAtoms(compound('and', [atom('a', 'equals', 1), atom('b', 'equals', 2)])).length // 2
|
|
1409
|
+
* ```
|
|
1410
|
+
*/
|
|
1411
|
+
export declare function extractAtoms(expression: Expression): readonly Atom[];
|
|
1412
|
+
|
|
1413
|
+
/**
|
|
1414
|
+
* Flatten a logical conclusion expression into its asserted `field = value`
|
|
1415
|
+
* pairs — connectives IGNORED.
|
|
1416
|
+
*
|
|
1417
|
+
* @remarks
|
|
1418
|
+
* The conclusion-extraction step of the logical reasoner's chaining: every
|
|
1419
|
+
* `atom` inside the expression asserts its `formatField(check.field) =
|
|
1420
|
+
* check.value` pair, and compounds are walked without regard to the connective
|
|
1421
|
+
* (an atom under `not` / `or` is asserted just the same). Later operands WIN on
|
|
1422
|
+
* a key clash (`Object.assign` order). Recursion runs through this exported
|
|
1423
|
+
* function itself; the derived-overlay keys are `formatField` strings (an array
|
|
1424
|
+
* field path flattens to its dot-joined form).
|
|
1425
|
+
*
|
|
1426
|
+
* @param expression - The conclusion expression to flatten
|
|
1427
|
+
* @returns A fresh record of asserted `field → value` pairs
|
|
1428
|
+
*
|
|
1429
|
+
* @example
|
|
1430
|
+
* ```ts
|
|
1431
|
+
* import { atom, compound, extractConclusions } from '@src/core'
|
|
1432
|
+
*
|
|
1433
|
+
* extractConclusions(atom('adult', 'equals', true)) // { adult: true }
|
|
1434
|
+
* extractConclusions(compound('and', [atom('a', 'equals', 1), atom('b', 'equals', 2)])) // { a: 1, b: 2 }
|
|
1435
|
+
* ```
|
|
1436
|
+
*/
|
|
1437
|
+
export declare function extractConclusions(expression: Expression): Record<string, unknown>;
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* One fact: a `predicate` over positional `terms`.
|
|
1441
|
+
*
|
|
1442
|
+
* @remarks
|
|
1443
|
+
* A string term starting with `?` is a unification variable (the prefix is
|
|
1444
|
+
* part of its name). `confidence` is `0–1` and propagates multiplicatively
|
|
1445
|
+
* through derivations (default `1`).
|
|
1446
|
+
*/
|
|
1447
|
+
export declare interface Fact {
|
|
1448
|
+
readonly id: string;
|
|
1449
|
+
readonly predicate: string;
|
|
1450
|
+
readonly terms: readonly unknown[];
|
|
1451
|
+
readonly confidence?: number;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
/**
|
|
1455
|
+
* Build a {@link Fact} — a predicate over positional terms.
|
|
1456
|
+
*
|
|
1457
|
+
* @remarks
|
|
1458
|
+
* `confidence` defaults to `1` (the key is always set). A string term starting
|
|
1459
|
+
* with `?` is a unification variable.
|
|
1460
|
+
*
|
|
1461
|
+
* @param id - The fact id
|
|
1462
|
+
* @param predicate - The predicate name
|
|
1463
|
+
* @param terms - The positional terms
|
|
1464
|
+
* @param confidence - The fact's confidence (`0–1`, defaults to `1`)
|
|
1465
|
+
* @returns A fresh fact
|
|
1466
|
+
*
|
|
1467
|
+
* @example
|
|
1468
|
+
* ```ts
|
|
1469
|
+
* import { fact } from '@src/core'
|
|
1470
|
+
*
|
|
1471
|
+
* fact('f1', 'human', ['socrates']) // confidence 1
|
|
1472
|
+
* fact('f2', 'laysEggs', ['tweety'], 0.9) // explicit confidence
|
|
1473
|
+
* ```
|
|
1474
|
+
*/
|
|
1475
|
+
export declare function fact(id: string, predicate: string, terms: readonly unknown[], confidence?: number): Fact;
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* The {@link FactManagerInterface} implementation — a self-owning, kind-free
|
|
1479
|
+
* manager over an inferential definition's `facts`.
|
|
1480
|
+
*
|
|
1481
|
+
* @remarks
|
|
1482
|
+
* OWNS its `#facts` collection as private copy-on-write state and its own
|
|
1483
|
+
* {@link Emitter} over {@link FactManagerEventMap}. `Fact.id` is an AUTHORING
|
|
1484
|
+
* label — the runtime content-dedups facts by predicate+arity+terms,
|
|
1485
|
+
* independently of this manager's id-keyed dedup. The write-only `collection`
|
|
1486
|
+
* setter is the owning builder's silent bulk re-seat channel (used by `merge`).
|
|
1487
|
+
* `destroy()` is idempotent and tears the emitter down LAST; any other call
|
|
1488
|
+
* after it throws `ReasonError('DESTROYED', …)`.
|
|
1489
|
+
*/
|
|
1490
|
+
export declare class FactManager implements FactManagerInterface {
|
|
1491
|
+
#private;
|
|
1492
|
+
constructor(options?: FactManagerOptions);
|
|
1493
|
+
get emitter(): EmitterInterface<FactManagerEventMap>;
|
|
1494
|
+
set collection(value: readonly Fact[]);
|
|
1495
|
+
fact(id: string): Fact | undefined;
|
|
1496
|
+
facts(): readonly Fact[];
|
|
1497
|
+
append(fact: Fact, target?: string): void;
|
|
1498
|
+
prepend(fact: Fact, target?: string): void;
|
|
1499
|
+
replace(fact: Fact): void;
|
|
1500
|
+
remove(id: string): void;
|
|
1501
|
+
destroy(): void;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
/** The push observation surface of a {@link FactManagerInterface} (AGENTS §13). */
|
|
1505
|
+
export declare type FactManagerEventMap = {
|
|
1506
|
+
/** A fact was appended — carries its id. */
|
|
1507
|
+
readonly append: readonly [id: string];
|
|
1508
|
+
/** A fact was prepended — carries its id. */
|
|
1509
|
+
readonly prepend: readonly [id: string];
|
|
1510
|
+
/** A fact was replaced in place — carries its id. */
|
|
1511
|
+
readonly replace: readonly [id: string];
|
|
1512
|
+
/** A fact was removed — carries its id. */
|
|
1513
|
+
readonly remove: readonly [id: string];
|
|
1514
|
+
/** The manager was destroyed. */
|
|
1515
|
+
readonly destroy: readonly [];
|
|
1516
|
+
};
|
|
1517
|
+
|
|
1518
|
+
/**
|
|
1519
|
+
* The {@link DefinitionBuilderInterface} manager over an inferential
|
|
1520
|
+
* definition's `facts` — a self-owning, kind-free collection manager.
|
|
1521
|
+
*/
|
|
1522
|
+
export declare interface FactManagerInterface {
|
|
1523
|
+
readonly emitter: EmitterInterface<FactManagerEventMap>;
|
|
1524
|
+
set collection(value: readonly Fact[]);
|
|
1525
|
+
fact(id: string): Fact | undefined;
|
|
1526
|
+
facts(): readonly Fact[];
|
|
1527
|
+
append(fact: Fact, target?: string): void;
|
|
1528
|
+
prepend(fact: Fact, target?: string): void;
|
|
1529
|
+
replace(fact: Fact): void;
|
|
1530
|
+
remove(id: string): void;
|
|
1531
|
+
destroy(): void;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
/**
|
|
1535
|
+
* Options for `createFactManager` / the `FactManager` constructor.
|
|
1536
|
+
*
|
|
1537
|
+
* @remarks
|
|
1538
|
+
* `facts` — the initial collection (defaults to empty). `on` — initial event
|
|
1539
|
+
* listeners (AGENTS §8). `error` — the emitter's listener-error handler
|
|
1540
|
+
* (AGENTS §13).
|
|
1541
|
+
*/
|
|
1542
|
+
export declare interface FactManagerOptions {
|
|
1543
|
+
readonly facts?: readonly Fact[];
|
|
1544
|
+
readonly on?: EmitterHooks<FactManagerEventMap>;
|
|
1545
|
+
readonly error?: EmitterErrorHandler;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
/**
|
|
1549
|
+
* One scored input of a quantitative group.
|
|
1550
|
+
*
|
|
1551
|
+
* @remarks
|
|
1552
|
+
* Evaluated as a pipeline: `checks` gate (ALL must be met) → `source` resolve
|
|
1553
|
+
* (`fallback` when unresolvable) → finite check → `transforms` chain →
|
|
1554
|
+
* `bounds` clamp → finite recheck. `weight` participates only at group
|
|
1555
|
+
* aggregation (default `1`); `priority` orders evaluation ascending (default
|
|
1556
|
+
* `0`, stable); `enabled: false` skips the factor entirely (omitted from
|
|
1557
|
+
* results); `required: true` promotes a gate / resolution failure to a result
|
|
1558
|
+
* error (making `success` false) without aborting the run.
|
|
1559
|
+
*/
|
|
1560
|
+
export declare interface Factor {
|
|
1561
|
+
readonly id: string;
|
|
1562
|
+
readonly name: string;
|
|
1563
|
+
readonly description?: string;
|
|
1564
|
+
readonly source: Source;
|
|
1565
|
+
readonly fallback?: number;
|
|
1566
|
+
readonly checks?: readonly Check[];
|
|
1567
|
+
readonly transforms?: readonly Transform[];
|
|
1568
|
+
readonly bounds?: Bounds;
|
|
1569
|
+
readonly weight?: number;
|
|
1570
|
+
readonly priority?: number;
|
|
1571
|
+
readonly enabled?: boolean;
|
|
1572
|
+
readonly required?: boolean;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
/**
|
|
1576
|
+
* A group of factors aggregated into one value.
|
|
1577
|
+
*
|
|
1578
|
+
* @remarks
|
|
1579
|
+
* The group value is `base` (default `0`) plus the aggregation of its APPLIED
|
|
1580
|
+
* factors' values (with per-factor weights), clamped to `bounds` — never
|
|
1581
|
+
* rounded. `strict: true` makes the group all-or-nothing: if any evaluated
|
|
1582
|
+
* factor did not apply, the group contributes only its `base` and reports
|
|
1583
|
+
* `applied: false`. A group with zero applied factors is excluded from the
|
|
1584
|
+
* definition-level aggregation.
|
|
1585
|
+
*/
|
|
1586
|
+
export declare interface FactorGroup {
|
|
1587
|
+
readonly id: string;
|
|
1588
|
+
readonly name: string;
|
|
1589
|
+
readonly description?: string;
|
|
1590
|
+
readonly factors: readonly Factor[];
|
|
1591
|
+
readonly aggregation: Aggregation;
|
|
1592
|
+
readonly base?: number;
|
|
1593
|
+
readonly bounds?: Bounds;
|
|
1594
|
+
readonly enabled?: boolean;
|
|
1595
|
+
readonly strict?: boolean;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
/**
|
|
1599
|
+
* Build a {@link FactorGroup}.
|
|
1600
|
+
*
|
|
1601
|
+
* @remarks
|
|
1602
|
+
* `name` defaults to the `id`; set `name`, `description`, `base`, `bounds`,
|
|
1603
|
+
* `enabled`, or `strict` through `overrides`.
|
|
1604
|
+
*
|
|
1605
|
+
* @param id - The group id
|
|
1606
|
+
* @param aggregation - How the applied factors' values reduce to one
|
|
1607
|
+
* @param factors - The group's factors
|
|
1608
|
+
* @param overrides - Optional {@link FactorGroup} fields merged over the defaults
|
|
1609
|
+
* @returns A fresh factor group
|
|
1610
|
+
*
|
|
1611
|
+
* @example
|
|
1612
|
+
* ```ts
|
|
1613
|
+
* import { factorGroup, staticFactor } from '@src/core'
|
|
1614
|
+
*
|
|
1615
|
+
* factorGroup('g1', 'sum', [staticFactor('f1', 10)], { base: 100 })
|
|
1616
|
+
* ```
|
|
1617
|
+
*/
|
|
1618
|
+
export declare function factorGroup(id: string, aggregation: Aggregation, factors: readonly Factor[], overrides?: Partial<Omit<FactorGroup, 'id' | 'aggregation' | 'factors'>>): FactorGroup;
|
|
1619
|
+
|
|
1620
|
+
/**
|
|
1621
|
+
* The {@link FactorManagerInterface} implementation — the sole DIVERGENT
|
|
1622
|
+
* manager: factors nest inside groups, so it holds NO collection state of its
|
|
1623
|
+
* own and threads a required `groupId` locator.
|
|
1624
|
+
*
|
|
1625
|
+
* @remarks
|
|
1626
|
+
* Constructor-injected with the sibling {@link GroupManagerInterface}: each
|
|
1627
|
+
* write verb reads the located group (`groups.group(groupId)`), applies the
|
|
1628
|
+
* factor-level pure helper ({@link appendFactor} etc.), and writes the updated
|
|
1629
|
+
* group back via `groups.replace(…)`. A `groupId` naming no existing group
|
|
1630
|
+
* throws `ReasonError('TARGET', …, { groupId })`. It still owns its OWN
|
|
1631
|
+
* {@link Emitter} over {@link FactorManagerEventMap} (factor-id payloads).
|
|
1632
|
+
* `destroy()` is idempotent and tears the emitter down LAST; any other call
|
|
1633
|
+
* after it throws `ReasonError('DESTROYED', …)`.
|
|
1634
|
+
*/
|
|
1635
|
+
export declare class FactorManager implements FactorManagerInterface {
|
|
1636
|
+
#private;
|
|
1637
|
+
constructor(groups: GroupManagerInterface, options?: FactorManagerOptions);
|
|
1638
|
+
get emitter(): EmitterInterface<FactorManagerEventMap>;
|
|
1639
|
+
factor(groupId: string, id: string): Factor | undefined;
|
|
1640
|
+
factors(groupId: string): readonly Factor[];
|
|
1641
|
+
append(groupId: string, factor: Factor, target?: string): void;
|
|
1642
|
+
prepend(groupId: string, factor: Factor, target?: string): void;
|
|
1643
|
+
replace(groupId: string, factor: Factor): void;
|
|
1644
|
+
remove(groupId: string, id: string): void;
|
|
1645
|
+
destroy(): void;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/** The push observation surface of a {@link FactorManagerInterface} (AGENTS §13). */
|
|
1649
|
+
export declare type FactorManagerEventMap = {
|
|
1650
|
+
/** A factor was appended — carries its id. */
|
|
1651
|
+
readonly append: readonly [id: string];
|
|
1652
|
+
/** A factor was prepended — carries its id. */
|
|
1653
|
+
readonly prepend: readonly [id: string];
|
|
1654
|
+
/** A factor was replaced in place — carries its id. */
|
|
1655
|
+
readonly replace: readonly [id: string];
|
|
1656
|
+
/** A factor was removed — carries its id. */
|
|
1657
|
+
readonly remove: readonly [id: string];
|
|
1658
|
+
/** The manager was destroyed. */
|
|
1659
|
+
readonly destroy: readonly [];
|
|
1660
|
+
};
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
* The {@link DefinitionBuilderInterface} manager over a `FactorGroup`'s
|
|
1664
|
+
* `factors`, threaded through the required `groupId` locator (a factor lives
|
|
1665
|
+
* inside its group).
|
|
1666
|
+
*
|
|
1667
|
+
* @remarks
|
|
1668
|
+
* Divergent from the other managers: factors nest inside groups, so this
|
|
1669
|
+
* manager holds NO collection state of its own — it reads and writes through
|
|
1670
|
+
* the sibling {@link GroupManagerInterface} (`groups.group(groupId)` then
|
|
1671
|
+
* `groups.replace(…)`). A `groupId` naming no existing group throws
|
|
1672
|
+
* `ReasonError('TARGET', …, { groupId })`. `append` / `prepend` additionally
|
|
1673
|
+
* take an optional `target` factor id (a naming miss throws
|
|
1674
|
+
* `ReasonError('TARGET', …)`). It still owns its OWN emitter (factor-id
|
|
1675
|
+
* payloads).
|
|
1676
|
+
*/
|
|
1677
|
+
export declare interface FactorManagerInterface {
|
|
1678
|
+
readonly emitter: EmitterInterface<FactorManagerEventMap>;
|
|
1679
|
+
factor(groupId: string, id: string): Factor | undefined;
|
|
1680
|
+
factors(groupId: string): readonly Factor[];
|
|
1681
|
+
append(groupId: string, factor: Factor, target?: string): void;
|
|
1682
|
+
prepend(groupId: string, factor: Factor, target?: string): void;
|
|
1683
|
+
replace(groupId: string, factor: Factor): void;
|
|
1684
|
+
remove(groupId: string, id: string): void;
|
|
1685
|
+
destroy(): void;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
/**
|
|
1689
|
+
* Options for `createFactorManager` / the `FactorManager` constructor.
|
|
1690
|
+
*
|
|
1691
|
+
* @remarks
|
|
1692
|
+
* The sibling `GroupManagerInterface` reference is a constructor argument, not
|
|
1693
|
+
* an option. `on` — initial event listeners (AGENTS §8). `error` — the
|
|
1694
|
+
* emitter's listener-error handler (AGENTS §13).
|
|
1695
|
+
*/
|
|
1696
|
+
export declare interface FactorManagerOptions {
|
|
1697
|
+
readonly on?: EmitterHooks<FactorManagerEventMap>;
|
|
1698
|
+
readonly error?: EmitterErrorHandler;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
/** One band of a {@link RangeSource} — an optional inclusive bounds test and the value it yields. */
|
|
1702
|
+
export declare interface FactorRange {
|
|
1703
|
+
readonly bounds?: Bounds;
|
|
1704
|
+
readonly value: number;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
/**
|
|
1708
|
+
* One factor's evaluation outcome.
|
|
1709
|
+
*
|
|
1710
|
+
* @remarks
|
|
1711
|
+
* `raw` is the resolved source value before transforms / clamping (absent when
|
|
1712
|
+
* the source never resolved); `checks` is present only when the factor
|
|
1713
|
+
* declared checks and they gated it out. An unapplied factor carries
|
|
1714
|
+
* `value: 0`.
|
|
1715
|
+
*/
|
|
1716
|
+
export declare interface FactorResult {
|
|
1717
|
+
readonly id: string;
|
|
1718
|
+
readonly applied: boolean;
|
|
1719
|
+
readonly value: number;
|
|
1720
|
+
readonly raw?: number;
|
|
1721
|
+
readonly checks?: readonly CheckResult[];
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
/**
|
|
1725
|
+
* Derive a fact's predicate+arity bucket key — length-prefixed so the
|
|
1726
|
+
* delimiter cannot be forged.
|
|
1727
|
+
*
|
|
1728
|
+
* @remarks
|
|
1729
|
+
* Keys both stored facts and premise patterns (both are `Fact`-shaped) for
|
|
1730
|
+
* {@link indexByArity}: `matchFacts` already rejects an arity mismatch, so
|
|
1731
|
+
* narrowing a same-predicate bucket to same-predicate-AND-same-arity only
|
|
1732
|
+
* excludes candidates that could never unify anyway. Only `predicate` — the
|
|
1733
|
+
* one free-form, adversary-controlled part — is length-prefixed
|
|
1734
|
+
* (`length + ':' + predicate`), mirroring {@link factToKey}'s framing so a
|
|
1735
|
+
* predicate string embedding the `' '` delimiter can never be mistaken for a
|
|
1736
|
+
* different predicate+arity pairing; `terms.length` is always a plain
|
|
1737
|
+
* non-negative integer (never itself contains a space) so it needs no prefix.
|
|
1738
|
+
*
|
|
1739
|
+
* @param source - The fact (or premise pattern) to key
|
|
1740
|
+
* @returns The predicate+arity key string
|
|
1741
|
+
*
|
|
1742
|
+
* @example
|
|
1743
|
+
* ```ts
|
|
1744
|
+
* import { fact, factToArityKey } from '@src/core'
|
|
1745
|
+
*
|
|
1746
|
+
* factToArityKey(fact('a', 'human', ['x'])) // arity 1
|
|
1747
|
+
* factToArityKey(fact('b', 'human', ['x', 'y'])) // arity 2 — distinct key
|
|
1748
|
+
* ```
|
|
1749
|
+
*/
|
|
1750
|
+
export declare function factToArityKey(source: Fact): string;
|
|
1751
|
+
|
|
1752
|
+
/**
|
|
1753
|
+
* Derive a fact's canonical dedup key — predicate + arity + per-term
|
|
1754
|
+
* SameValueZero identity (confidence is NOT part of it).
|
|
1755
|
+
*
|
|
1756
|
+
* @remarks
|
|
1757
|
+
* The dedup key of the inferential reasoner's forward fixpoint: two facts with
|
|
1758
|
+
* the same predicate, arity, and SameValueZero-equal terms share a key (so a
|
|
1759
|
+
* NaN-term fact derives once and ±0 collapse keeping the first), while
|
|
1760
|
+
* confidence never enters the key. Each part — the predicate, the stringified
|
|
1761
|
+
* arity, and every {@link termToKey} — is LENGTH-PREFIXED (`length + ':' + part`)
|
|
1762
|
+
* before joining, so the delimiter can never be forged by an adversarial string
|
|
1763
|
+
* term embedding it: two distinct facts always produce distinct keys, even when
|
|
1764
|
+
* a term string contains the delimiter (an injective framing raw joining lacked).
|
|
1765
|
+
*
|
|
1766
|
+
* @param source - The fact to key
|
|
1767
|
+
* @param identities - The reference-identity map threaded across the dedupe pass (see {@link termToKey})
|
|
1768
|
+
* @returns The fact's canonical key string
|
|
1769
|
+
*
|
|
1770
|
+
* @example
|
|
1771
|
+
* ```ts
|
|
1772
|
+
* import { fact, factToKey } from '@src/core'
|
|
1773
|
+
*
|
|
1774
|
+
* const identities = new Map<object, number>()
|
|
1775
|
+
* // Same predicate + terms → same key regardless of confidence:
|
|
1776
|
+
* factToKey(fact('a', 'p', ['x'], 1), identities) === factToKey(fact('b', 'p', ['x'], 0.5), identities)
|
|
1777
|
+
* ```
|
|
1778
|
+
*/
|
|
1779
|
+
export declare function factToKey(source: Fact, identities: Map<object, number>): string;
|
|
1780
|
+
|
|
1781
|
+
/**
|
|
1782
|
+
* Build a {@link Factor} over a field {@link Source}.
|
|
1783
|
+
*
|
|
1784
|
+
* @param id - The factor id
|
|
1785
|
+
* @param field - The subject field to resolve as a number
|
|
1786
|
+
* @param overrides - Optional {@link Factor} fields merged over the defaults
|
|
1787
|
+
* @returns A fresh factor
|
|
1788
|
+
*
|
|
1789
|
+
* @example
|
|
1790
|
+
* ```ts
|
|
1791
|
+
* import { fieldFactor, transform } from '@src/core'
|
|
1792
|
+
*
|
|
1793
|
+
* fieldFactor('income-score', 'income', { transforms: [transform('divide', 1000)], fallback: 0 })
|
|
1794
|
+
* ```
|
|
1795
|
+
*/
|
|
1796
|
+
export declare function fieldFactor(id: string, field: FieldPath, overrides?: Partial<Omit<Factor, 'id' | 'source'>>): Factor;
|
|
1797
|
+
|
|
1798
|
+
/**
|
|
1799
|
+
* A factor source reading a subject field as a number.
|
|
1800
|
+
*
|
|
1801
|
+
* @remarks
|
|
1802
|
+
* The field is coerced with the contracts `parseNumber` — a finite number
|
|
1803
|
+
* passes through, a numeric string coerces, and everything else (including
|
|
1804
|
+
* `NaN` / `±Infinity`) is unresolvable, falling back to the factor's
|
|
1805
|
+
* `fallback`.
|
|
1806
|
+
*/
|
|
1807
|
+
export declare interface FieldSource {
|
|
1808
|
+
readonly origin: 'field';
|
|
1809
|
+
readonly field: FieldPath;
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
/**
|
|
1813
|
+
* Build a field {@link Source} — a subject field read as a number.
|
|
1814
|
+
*
|
|
1815
|
+
* @param field - The subject field to resolve
|
|
1816
|
+
* @returns A fresh field source
|
|
1817
|
+
*
|
|
1818
|
+
* @example
|
|
1819
|
+
* ```ts
|
|
1820
|
+
* import { fieldSource } from '@src/core'
|
|
1821
|
+
*
|
|
1822
|
+
* fieldSource(['profile', 'score']) // descends into nested objects
|
|
1823
|
+
* ```
|
|
1824
|
+
*/
|
|
1825
|
+
export declare function fieldSource(field: FieldPath): Source;
|
|
1826
|
+
|
|
1827
|
+
/**
|
|
1828
|
+
* Collect the ids that appear MORE THAN ONCE in an id-carrying list — each
|
|
1829
|
+
* duplicated id reported once, in first-occurrence order.
|
|
1830
|
+
*
|
|
1831
|
+
* @remarks
|
|
1832
|
+
* The shared uniqueness scan behind every reasoner's `validate()` duplicate-id
|
|
1833
|
+
* WARNINGS (rules, groups, factors, equations, inferences). Runtime stays
|
|
1834
|
+
* permissive about duplicates (first/last-wins artifacts) — this helper only
|
|
1835
|
+
* surfaces them.
|
|
1836
|
+
*
|
|
1837
|
+
* @param items - The id-carrying items to scan
|
|
1838
|
+
* @returns The duplicated ids, once each
|
|
1839
|
+
*
|
|
1840
|
+
* @example
|
|
1841
|
+
* ```ts
|
|
1842
|
+
* import { findDuplicates } from '@src/core'
|
|
1843
|
+
*
|
|
1844
|
+
* findDuplicates([{ id: 'a' }, { id: 'b' }, { id: 'a' }, { id: 'a' }]) // ['a']
|
|
1845
|
+
* ```
|
|
1846
|
+
*/
|
|
1847
|
+
export declare function findDuplicates(items: readonly {
|
|
1848
|
+
readonly id: string;
|
|
1849
|
+
}[]): readonly string[];
|
|
1850
|
+
|
|
1851
|
+
/**
|
|
1852
|
+
* The `formatField`-flattened overlay keys an array-path conclusion atom
|
|
1853
|
+
* writes ANYWHERE among `rules` that an array-path premise atom also reads
|
|
1854
|
+
* ANYWHERE among `rules`.
|
|
1855
|
+
*
|
|
1856
|
+
* @remarks
|
|
1857
|
+
* The cross-rule authoring-time footgun probe behind
|
|
1858
|
+
* `LogicalReasoner.validate`'s overlay-key-mismatch warning: a logical
|
|
1859
|
+
* conclusion's derived overlay is a FLAT record keyed by
|
|
1860
|
+
* `formatField(check.field)` — an array `FieldPath` dot-joins into one string
|
|
1861
|
+
* key. A premise that reads the same field via a DOTTED-STRING path resolves
|
|
1862
|
+
* that flat key correctly, but a premise that reads it via an ARRAY path
|
|
1863
|
+
* calls `resolveField`, which descends key-by-key into nesting the flat
|
|
1864
|
+
* overlay never created, so the chain silently fails to connect. Collects the
|
|
1865
|
+
* flattened keys of every array-path conclusion atom across `rules` (once
|
|
1866
|
+
* each, authored order), then returns the ones that also appear as the
|
|
1867
|
+
* flattened key of some array-path premise atom anywhere in `rules` — a
|
|
1868
|
+
* single pass over every rule's conclusion and premise atoms, so the cost is
|
|
1869
|
+
* linear in total atom count, never quadratic.
|
|
1870
|
+
*
|
|
1871
|
+
* @param rules - The rules to scan for the array-path write/read overlap
|
|
1872
|
+
* @returns The mismatched overlay keys, once each, authored order
|
|
1873
|
+
*
|
|
1874
|
+
* @example
|
|
1875
|
+
* ```ts
|
|
1876
|
+
* import { atom, findOverlayMismatches, rule } from '@src/core'
|
|
1877
|
+
*
|
|
1878
|
+
* findOverlayMismatches([
|
|
1879
|
+
* rule('a', [], atom(['address', 'city'], 'equals', 'NYC')),
|
|
1880
|
+
* rule('b', [atom(['address', 'city'], 'equals', 'NYC')], atom('eligible', 'equals', true)),
|
|
1881
|
+
* ]) // ['address.city']
|
|
1882
|
+
* ```
|
|
1883
|
+
*/
|
|
1884
|
+
export declare function findOverlayMismatches(rules: readonly Rule[]): readonly string[];
|
|
1885
|
+
|
|
1886
|
+
/**
|
|
1887
|
+
* The `'?'`-prefixed variables an inference's conclusion introduces that no
|
|
1888
|
+
* premise binds.
|
|
1889
|
+
*
|
|
1890
|
+
* @remarks
|
|
1891
|
+
* The authoring-time footgun probe behind `InferentialReasoner.validate`'s
|
|
1892
|
+
* unbound-variable warning: backward proving establishes each premise
|
|
1893
|
+
* independently with no cross-premise binding consistency, so a conclusion
|
|
1894
|
+
* term that no premise's `terms` ever names stays uninstantiated in the
|
|
1895
|
+
* derived fact. Gathers the `?`-prefixed string terms of `conclusion.terms`,
|
|
1896
|
+
* subtracts every `?`-prefixed string term appearing in any premise's
|
|
1897
|
+
* `terms`, and returns the remainder once each, in the conclusion's authored
|
|
1898
|
+
* order.
|
|
1899
|
+
*
|
|
1900
|
+
* @param source - The inference whose conclusion is checked against its premises
|
|
1901
|
+
* @returns The unbound conclusion variable names, once each, authored order
|
|
1902
|
+
*
|
|
1903
|
+
* @example
|
|
1904
|
+
* ```ts
|
|
1905
|
+
* import { fact, findUnboundVariables, inference } from '@src/core'
|
|
1906
|
+
*
|
|
1907
|
+
* findUnboundVariables(
|
|
1908
|
+
* inference('i', 'I', [fact('p', 'human', ['?x'])], fact('c', 'mortal', ['?x', '?y'])),
|
|
1909
|
+
* ) // ['?y'] — '?x' is bound by the premise, '?y' is not
|
|
1910
|
+
* ```
|
|
1911
|
+
*/
|
|
1912
|
+
export declare function findUnboundVariables(source: Inference): readonly string[];
|
|
1913
|
+
|
|
1914
|
+
/**
|
|
1915
|
+
* Format a {@link FieldPath} for display — the single string key itself, or the
|
|
1916
|
+
* array segments joined with `.`.
|
|
1917
|
+
*
|
|
1918
|
+
* @remarks
|
|
1919
|
+
* Display-only: the joined form is how a field appears in traces and derived
|
|
1920
|
+
* overlays; it is NOT re-parsed into a path (a string stays ONE key).
|
|
1921
|
+
*
|
|
1922
|
+
* @param field - The field path to format
|
|
1923
|
+
* @returns The display string
|
|
1924
|
+
*
|
|
1925
|
+
* @example
|
|
1926
|
+
* ```ts
|
|
1927
|
+
* formatField('age') // 'age'
|
|
1928
|
+
* formatField(['address', 'city']) // 'address.city'
|
|
1929
|
+
* ```
|
|
1930
|
+
*/
|
|
1931
|
+
export declare function formatField(field: FieldPath): string;
|
|
1932
|
+
|
|
1933
|
+
/**
|
|
1934
|
+
* The {@link GroupManagerInterface} implementation — a self-owning, kind-free
|
|
1935
|
+
* manager over a quantitative definition's `groups`.
|
|
1936
|
+
*
|
|
1937
|
+
* @remarks
|
|
1938
|
+
* OWNS its `#groups` collection as private copy-on-write state and its own
|
|
1939
|
+
* {@link Emitter} over {@link GroupManagerEventMap}. Every write verb delegates
|
|
1940
|
+
* to the matching collection-level pure helper ({@link appendById} etc.),
|
|
1941
|
+
* reassigns the fresh array, then emits (the affected group id) AFTER the
|
|
1942
|
+
* mutation. The write-only `collection` setter is the owning builder's silent
|
|
1943
|
+
* bulk re-seat channel (used by `merge`). `destroy()` is idempotent and tears
|
|
1944
|
+
* the emitter down LAST; any other call after it throws
|
|
1945
|
+
* `ReasonError('DESTROYED', …)`.
|
|
1946
|
+
*/
|
|
1947
|
+
export declare class GroupManager implements GroupManagerInterface {
|
|
1948
|
+
#private;
|
|
1949
|
+
constructor(options?: GroupManagerOptions);
|
|
1950
|
+
get emitter(): EmitterInterface<GroupManagerEventMap>;
|
|
1951
|
+
set collection(value: readonly FactorGroup[]);
|
|
1952
|
+
group(id: string): FactorGroup | undefined;
|
|
1953
|
+
groups(): readonly FactorGroup[];
|
|
1954
|
+
append(group: FactorGroup, target?: string): void;
|
|
1955
|
+
prepend(group: FactorGroup, target?: string): void;
|
|
1956
|
+
replace(group: FactorGroup): void;
|
|
1957
|
+
remove(id: string): void;
|
|
1958
|
+
destroy(): void;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
/** The push observation surface of a {@link GroupManagerInterface} (AGENTS §13). */
|
|
1962
|
+
export declare type GroupManagerEventMap = {
|
|
1963
|
+
/** A group was appended — carries its id. */
|
|
1964
|
+
readonly append: readonly [id: string];
|
|
1965
|
+
/** A group was prepended — carries its id. */
|
|
1966
|
+
readonly prepend: readonly [id: string];
|
|
1967
|
+
/** A group was replaced in place — carries its id. */
|
|
1968
|
+
readonly replace: readonly [id: string];
|
|
1969
|
+
/** A group was removed — carries its id. */
|
|
1970
|
+
readonly remove: readonly [id: string];
|
|
1971
|
+
/** The manager was destroyed. */
|
|
1972
|
+
readonly destroy: readonly [];
|
|
1973
|
+
};
|
|
1974
|
+
|
|
1975
|
+
/**
|
|
1976
|
+
* The {@link DefinitionBuilderInterface} manager over a quantitative
|
|
1977
|
+
* definition's `groups` — a self-owning, kind-free collection manager.
|
|
1978
|
+
*
|
|
1979
|
+
* @remarks
|
|
1980
|
+
* `append` / `prepend` place a group relative to an optional `target` id (a
|
|
1981
|
+
* naming miss throws `ReasonError('TARGET', …)`); `replace` swaps a same-id
|
|
1982
|
+
* group in place; `remove` filters a group out (no-op when absent).
|
|
1983
|
+
*/
|
|
1984
|
+
export declare interface GroupManagerInterface {
|
|
1985
|
+
readonly emitter: EmitterInterface<GroupManagerEventMap>;
|
|
1986
|
+
set collection(value: readonly FactorGroup[]);
|
|
1987
|
+
group(id: string): FactorGroup | undefined;
|
|
1988
|
+
groups(): readonly FactorGroup[];
|
|
1989
|
+
append(group: FactorGroup, target?: string): void;
|
|
1990
|
+
prepend(group: FactorGroup, target?: string): void;
|
|
1991
|
+
replace(group: FactorGroup): void;
|
|
1992
|
+
remove(id: string): void;
|
|
1993
|
+
destroy(): void;
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
/**
|
|
1997
|
+
* Options for `createGroupManager` / the `GroupManager` constructor.
|
|
1998
|
+
*
|
|
1999
|
+
* @remarks
|
|
2000
|
+
* `groups` — the initial collection (defaults to empty). `on` — initial event
|
|
2001
|
+
* listeners (AGENTS §8). `error` — the emitter's listener-error handler
|
|
2002
|
+
* (AGENTS §13).
|
|
2003
|
+
*/
|
|
2004
|
+
export declare interface GroupManagerOptions {
|
|
2005
|
+
readonly groups?: readonly FactorGroup[];
|
|
2006
|
+
readonly on?: EmitterHooks<GroupManagerEventMap>;
|
|
2007
|
+
readonly error?: EmitterErrorHandler;
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
/**
|
|
2011
|
+
* One group's evaluation outcome — its clamped value and the per-factor
|
|
2012
|
+
* results (disabled factors omitted entirely).
|
|
2013
|
+
*
|
|
2014
|
+
* @remarks
|
|
2015
|
+
* An UNAPPLIED group's `value` may be non-finite (a `minimum` / `maximum`
|
|
2016
|
+
* aggregation over zero applied factors is `base + NaN`) — it is excluded from
|
|
2017
|
+
* the definition-level aggregation, so only an APPLIED non-finite value reaches
|
|
2018
|
+
* the definition-level finite check.
|
|
2019
|
+
*/
|
|
2020
|
+
export declare interface GroupResult {
|
|
2021
|
+
readonly id: string;
|
|
2022
|
+
readonly applied: boolean;
|
|
2023
|
+
readonly value: number;
|
|
2024
|
+
readonly factors: readonly FactorResult[];
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
/**
|
|
2028
|
+
* Bucket facts by predicate+arity, preserving append order within each bucket.
|
|
2029
|
+
*
|
|
2030
|
+
* @remarks
|
|
2031
|
+
* The index behind the inferential reasoner's same-predicate-and-arity join
|
|
2032
|
+
* scans (`#findAllBindings` / `#calculatePremiseConfidence`): `matchFacts`
|
|
2033
|
+
* already rejects a predicate OR arity mismatch, so restricting a premise's
|
|
2034
|
+
* search to its own predicate+arity bucket changes nothing but the cost — the
|
|
2035
|
+
* surviving matches and their append order are identical to a predicate-only
|
|
2036
|
+
* index. Append order within a bucket is preserved, so a "first match wins"
|
|
2037
|
+
* scan finds the same fact a full linear pass would.
|
|
2038
|
+
*
|
|
2039
|
+
* @param facts - The facts to index
|
|
2040
|
+
* @returns A fresh `Map` from {@link factToArityKey} to its facts, in append order
|
|
2041
|
+
*
|
|
2042
|
+
* @example
|
|
2043
|
+
* ```ts
|
|
2044
|
+
* import { fact, indexByArity } from '@src/core'
|
|
2045
|
+
*
|
|
2046
|
+
* const index = indexByArity([fact('a', 'human', ['x']), fact('b', 'human', ['y'])])
|
|
2047
|
+
* index.get(factToArityKey(fact('c', 'human', ['z'])))?.length // 2
|
|
2048
|
+
* ```
|
|
2049
|
+
*/
|
|
2050
|
+
export declare function indexByArity(facts: readonly Fact[]): Map<string, Fact[]>;
|
|
2051
|
+
|
|
2052
|
+
/**
|
|
2053
|
+
* One inference rule: when every premise pattern unifies against known facts
|
|
2054
|
+
* (with consistent variable bindings), the instantiated `conclusion` is
|
|
2055
|
+
* derived.
|
|
2056
|
+
*
|
|
2057
|
+
* @remarks
|
|
2058
|
+
* A derived fact's confidence is the product of its matched premise facts'
|
|
2059
|
+
* confidences times the inference's own `confidence` (default `1`), rounded to
|
|
2060
|
+
* four decimal places. `enabled: false` skips the inference silently.
|
|
2061
|
+
*/
|
|
2062
|
+
export declare interface Inference {
|
|
2063
|
+
readonly id: string;
|
|
2064
|
+
readonly name: string;
|
|
2065
|
+
readonly description?: string;
|
|
2066
|
+
readonly premises: readonly Fact[];
|
|
2067
|
+
readonly conclusion: Fact;
|
|
2068
|
+
readonly confidence?: number;
|
|
2069
|
+
readonly enabled?: boolean;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
/**
|
|
2073
|
+
* Build an {@link Inference} — premise patterns and a conclusion pattern.
|
|
2074
|
+
*
|
|
2075
|
+
* @remarks
|
|
2076
|
+
* `name` defaults to the `id`; set `name`, `description`, `confidence`, or
|
|
2077
|
+
* `enabled` through `overrides`.
|
|
2078
|
+
*
|
|
2079
|
+
* @param id - The inference id
|
|
2080
|
+
* @param premises - The fact patterns that must ALL unify
|
|
2081
|
+
* @param conclusion - The fact pattern derived when they do
|
|
2082
|
+
* @param overrides - Optional {@link Inference} fields merged over the defaults
|
|
2083
|
+
* @returns A fresh inference
|
|
2084
|
+
*
|
|
2085
|
+
* @example
|
|
2086
|
+
* ```ts
|
|
2087
|
+
* import { fact, inference } from '@src/core'
|
|
2088
|
+
*
|
|
2089
|
+
* inference('mortal', [fact('p1', 'human', ['?x'])], fact('c1', 'mortal', ['?x']), { confidence: 0.8 })
|
|
2090
|
+
* ```
|
|
2091
|
+
*/
|
|
2092
|
+
export declare function inference(id: string, premises: readonly Fact[], conclusion: Fact, overrides?: Partial<Omit<Inference, 'id' | 'premises' | 'conclusion'>>): Inference;
|
|
2093
|
+
|
|
2094
|
+
/**
|
|
2095
|
+
* The {@link InferenceManagerInterface} implementation — a self-owning,
|
|
2096
|
+
* kind-free manager over an inferential definition's `inferences`.
|
|
2097
|
+
*
|
|
2098
|
+
* @remarks
|
|
2099
|
+
* OWNS its `#inferences` collection as private copy-on-write state and its own
|
|
2100
|
+
* {@link Emitter} over {@link InferenceManagerEventMap}. Inference order is
|
|
2101
|
+
* LOAD-BEARING — backward proving iterates in declaration order and returns on
|
|
2102
|
+
* first success. The write-only `collection` setter is the owning builder's
|
|
2103
|
+
* silent bulk re-seat channel (used by `merge`). `destroy()` is idempotent and
|
|
2104
|
+
* tears the emitter down LAST; any other call after it throws
|
|
2105
|
+
* `ReasonError('DESTROYED', …)`.
|
|
2106
|
+
*/
|
|
2107
|
+
export declare class InferenceManager implements InferenceManagerInterface {
|
|
2108
|
+
#private;
|
|
2109
|
+
constructor(options?: InferenceManagerOptions);
|
|
2110
|
+
get emitter(): EmitterInterface<InferenceManagerEventMap>;
|
|
2111
|
+
set collection(value: readonly Inference[]);
|
|
2112
|
+
inference(id: string): Inference | undefined;
|
|
2113
|
+
inferences(): readonly Inference[];
|
|
2114
|
+
append(inference: Inference, target?: string): void;
|
|
2115
|
+
prepend(inference: Inference, target?: string): void;
|
|
2116
|
+
replace(inference: Inference): void;
|
|
2117
|
+
remove(id: string): void;
|
|
2118
|
+
destroy(): void;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
/** The push observation surface of an {@link InferenceManagerInterface} (AGENTS §13). */
|
|
2122
|
+
export declare type InferenceManagerEventMap = {
|
|
2123
|
+
/** An inference was appended — carries its id. */
|
|
2124
|
+
readonly append: readonly [id: string];
|
|
2125
|
+
/** An inference was prepended — carries its id. */
|
|
2126
|
+
readonly prepend: readonly [id: string];
|
|
2127
|
+
/** An inference was replaced in place — carries its id. */
|
|
2128
|
+
readonly replace: readonly [id: string];
|
|
2129
|
+
/** An inference was removed — carries its id. */
|
|
2130
|
+
readonly remove: readonly [id: string];
|
|
2131
|
+
/** The manager was destroyed. */
|
|
2132
|
+
readonly destroy: readonly [];
|
|
2133
|
+
};
|
|
2134
|
+
|
|
2135
|
+
/**
|
|
2136
|
+
* The {@link DefinitionBuilderInterface} manager over an inferential
|
|
2137
|
+
* definition's `inferences` — a self-owning, kind-free collection manager.
|
|
2138
|
+
*
|
|
2139
|
+
* @remarks
|
|
2140
|
+
* Inference order is load-bearing — backward proving iterates in declaration
|
|
2141
|
+
* order and returns on first success.
|
|
2142
|
+
*/
|
|
2143
|
+
export declare interface InferenceManagerInterface {
|
|
2144
|
+
readonly emitter: EmitterInterface<InferenceManagerEventMap>;
|
|
2145
|
+
set collection(value: readonly Inference[]);
|
|
2146
|
+
inference(id: string): Inference | undefined;
|
|
2147
|
+
inferences(): readonly Inference[];
|
|
2148
|
+
append(inference: Inference, target?: string): void;
|
|
2149
|
+
prepend(inference: Inference, target?: string): void;
|
|
2150
|
+
replace(inference: Inference): void;
|
|
2151
|
+
remove(id: string): void;
|
|
2152
|
+
destroy(): void;
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
/**
|
|
2156
|
+
* Options for `createInferenceManager` / the `InferenceManager` constructor.
|
|
2157
|
+
*
|
|
2158
|
+
* @remarks
|
|
2159
|
+
* `inferences` — the initial collection (defaults to empty). `on` — initial
|
|
2160
|
+
* event listeners (AGENTS §8). `error` — the emitter's listener-error handler
|
|
2161
|
+
* (AGENTS §13).
|
|
2162
|
+
*/
|
|
2163
|
+
export declare interface InferenceManagerOptions {
|
|
2164
|
+
readonly inferences?: readonly Inference[];
|
|
2165
|
+
readonly on?: EmitterHooks<InferenceManagerEventMap>;
|
|
2166
|
+
readonly error?: EmitterErrorHandler;
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
/** Default `id` for an `InferentialReasoner`. */
|
|
2170
|
+
export declare const INFERENTIAL_ID = "inferential";
|
|
2171
|
+
|
|
2172
|
+
/**
|
|
2173
|
+
* An inferential (fact-derivation) definition.
|
|
2174
|
+
*
|
|
2175
|
+
* @remarks
|
|
2176
|
+
* `facts` is the base knowledge; scalar subject fields are additionally
|
|
2177
|
+
* injected as `has(key, value)` facts. `strategy` picks a forward fixpoint
|
|
2178
|
+
* (derive everything) or backward proving (first provable conclusion wins,
|
|
2179
|
+
* returning a proof tree); `depth` caps iterations / recursion (default `10`).
|
|
2180
|
+
*/
|
|
2181
|
+
export declare interface InferentialDefinition {
|
|
2182
|
+
readonly reasoning: 'inferential';
|
|
2183
|
+
readonly id: string;
|
|
2184
|
+
readonly name: string;
|
|
2185
|
+
readonly description?: string;
|
|
2186
|
+
readonly inferences: readonly Inference[];
|
|
2187
|
+
readonly facts: readonly Fact[];
|
|
2188
|
+
readonly strategy: ChainingStrategy;
|
|
2189
|
+
readonly depth?: number;
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
/**
|
|
2193
|
+
* Build an {@link InferentialDefinition}.
|
|
2194
|
+
*
|
|
2195
|
+
* @remarks
|
|
2196
|
+
* `strategy` defaults to `'forward'`; set `strategy`, `description`, or `depth`
|
|
2197
|
+
* through `overrides`.
|
|
2198
|
+
*
|
|
2199
|
+
* @param id - The definition id
|
|
2200
|
+
* @param name - The display name
|
|
2201
|
+
* @param facts - The base knowledge
|
|
2202
|
+
* @param inferences - The inference rules
|
|
2203
|
+
* @param overrides - Optional {@link InferentialDefinition} fields merged over the defaults
|
|
2204
|
+
* @returns A fresh inferential definition
|
|
2205
|
+
*
|
|
2206
|
+
* @example
|
|
2207
|
+
* ```ts
|
|
2208
|
+
* import { fact, inference, inferentialDefinition } from '@src/core'
|
|
2209
|
+
*
|
|
2210
|
+
* inferentialDefinition('mortality', 'Mortality', [fact('f1', 'human', ['socrates'])], [
|
|
2211
|
+
* inference('mortal', [fact('p1', 'human', ['?x'])], fact('c1', 'mortal', ['?x'])),
|
|
2212
|
+
* ])
|
|
2213
|
+
* ```
|
|
2214
|
+
*/
|
|
2215
|
+
export declare function inferentialDefinition(id: string, name: string, facts: readonly Fact[], inferences: readonly Inference[], overrides?: Partial<Omit<InferentialDefinition, 'reasoning' | 'id' | 'name' | 'facts' | 'inferences'>>): InferentialDefinition;
|
|
2216
|
+
|
|
2217
|
+
/**
|
|
2218
|
+
* The inferential reasoner — fact derivation with unification variables and
|
|
2219
|
+
* proof trees.
|
|
2220
|
+
*
|
|
2221
|
+
* @remarks
|
|
2222
|
+
* A string term starting with `?` is a unification variable — matching is
|
|
2223
|
+
* positional and BIDIRECTIONAL (variables may sit in facts as well as
|
|
2224
|
+
* patterns), with binding consistency enforced within one match and across
|
|
2225
|
+
* premises through pre-instantiation. Scalar subject fields (except `id`;
|
|
2226
|
+
* `null` / `undefined` / objects / arrays skipped) inject as
|
|
2227
|
+
* `has(key, value)` facts. Forward chaining is a naive fixpoint capped at
|
|
2228
|
+
* `depth` iterations: every consistent premise unification derives the
|
|
2229
|
+
* instantiated conclusion, with confidence = Π matched premise-fact confidences
|
|
2230
|
+
* × the inference's own `confidence`, rounded to four decimal places, and
|
|
2231
|
+
* duplicate facts (same predicate, arity, and SameValueZero-equal terms — a
|
|
2232
|
+
* NaN term derives once and converges) are never re-derived. Backward chaining
|
|
2233
|
+
* proves each inference's conclusion in
|
|
2234
|
+
* declaration order and RETURNS ON THE FIRST SUCCESS with one derived fact
|
|
2235
|
+
* (confidence = the inference's own — premise confidences are NOT propagated)
|
|
2236
|
+
* plus its {@link ProofNode} tree; recursion is guarded only by the depth cap.
|
|
2237
|
+
* Deriving nothing is still success. Nothing mutates its inputs; fully
|
|
2238
|
+
* deterministic (AGENTS §11).
|
|
2239
|
+
*/
|
|
2240
|
+
export declare class InferentialReasoner implements ReasonerInterface {
|
|
2241
|
+
#private;
|
|
2242
|
+
constructor(options?: InferentialReasonerOptions);
|
|
2243
|
+
get id(): string;
|
|
2244
|
+
get reasoning(): Reasoning;
|
|
2245
|
+
supports(definition: Definition): boolean;
|
|
2246
|
+
validate(definition: Definition): ReasonValidationResult;
|
|
2247
|
+
reason(subject: Subject, definition: Definition): ReasonResult;
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
/**
|
|
2251
|
+
* Options for `createInferentialReasoner` / the `InferentialReasoner`
|
|
2252
|
+
* constructor.
|
|
2253
|
+
*
|
|
2254
|
+
* @remarks
|
|
2255
|
+
* `id` — the reasoner's identity string (defaults to `INFERENTIAL_ID`).
|
|
2256
|
+
*/
|
|
2257
|
+
export declare interface InferentialReasonerOptions {
|
|
2258
|
+
readonly id?: string;
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
/**
|
|
2262
|
+
* The outcome of inferential reasoning.
|
|
2263
|
+
*
|
|
2264
|
+
* @remarks
|
|
2265
|
+
* `derived` lists the newly derived facts (deriving nothing is still success);
|
|
2266
|
+
* `proof` is produced only by the backward strategy, and only when a
|
|
2267
|
+
* conclusion was proved.
|
|
2268
|
+
*/
|
|
2269
|
+
export declare interface InferentialResult {
|
|
2270
|
+
readonly reasoning: 'inferential';
|
|
2271
|
+
readonly derived: readonly Fact[];
|
|
2272
|
+
readonly proof?: ProofNode;
|
|
2273
|
+
readonly success: boolean;
|
|
2274
|
+
readonly trace: readonly string[];
|
|
2275
|
+
readonly errors: readonly string[];
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
/**
|
|
2279
|
+
* Substitute a fact's bound `'?'`-variables with their values — a fresh fact
|
|
2280
|
+
* with unbound terms passed through unchanged.
|
|
2281
|
+
*
|
|
2282
|
+
* @remarks
|
|
2283
|
+
* The pattern-instantiation step of the inferential reasoner: a `'?'`-prefixed
|
|
2284
|
+
* string term that is present in `bindings` is replaced by its bound value;
|
|
2285
|
+
* every other term (constants and UNBOUND variables alike) is kept verbatim. The
|
|
2286
|
+
* returned fact is a fresh copy (`{ ...fact, terms }`) — the input is never
|
|
2287
|
+
* mutated (AGENTS §11).
|
|
2288
|
+
*
|
|
2289
|
+
* @param source - The fact (or pattern) to instantiate
|
|
2290
|
+
* @param bindings - The variable bindings to apply
|
|
2291
|
+
* @returns A fresh fact with bound variables substituted
|
|
2292
|
+
*
|
|
2293
|
+
* @example
|
|
2294
|
+
* ```ts
|
|
2295
|
+
* import { fact, instantiateFact } from '@src/core'
|
|
2296
|
+
*
|
|
2297
|
+
* instantiateFact(fact('c', 'mortal', ['?x']), { '?x': 'socrates' }).terms // ['socrates']
|
|
2298
|
+
* ```
|
|
2299
|
+
*/
|
|
2300
|
+
export declare function instantiateFact(source: Fact, bindings: Record<string, unknown>): Fact;
|
|
2301
|
+
|
|
2302
|
+
/**
|
|
2303
|
+
* The math operations the symbolic reasoner can invert while isolating a
|
|
2304
|
+
* target variable — anything else (a `power`, an `abs`) fails the equation
|
|
2305
|
+
* with a non-invertible error.
|
|
2306
|
+
*/
|
|
2307
|
+
export declare const INVERTIBLE_OPERATIONS: ReadonlySet<MathOperation>;
|
|
2308
|
+
|
|
2309
|
+
/**
|
|
2310
|
+
* Invert a `x op right = value` step, solving for the LEFT operand `x`.
|
|
2311
|
+
*
|
|
2312
|
+
* @remarks
|
|
2313
|
+
* The left-operand inverse of the symbolic reasoner's isolation: `add` inverts
|
|
2314
|
+
* to subtraction, `subtract` to addition, `multiply` to division, `divide` to
|
|
2315
|
+
* multiplication. Inversion by zero yields `NaN` (never a throw) — a `multiply`
|
|
2316
|
+
* with a zero `right` has no unique solution, and `x / 0 = value` has none
|
|
2317
|
+
* either, so both surface `NaN` for the non-finite check to report rather than a
|
|
2318
|
+
* bogus value. A non-invertible operator throws (caught per equation upstream).
|
|
2319
|
+
*
|
|
2320
|
+
* @param operator - The math operation to invert
|
|
2321
|
+
* @param value - The known result of `x op right`
|
|
2322
|
+
* @param rightValue - The known right operand
|
|
2323
|
+
* @returns The isolated left operand (`NaN` on a zero-division inverse)
|
|
2324
|
+
*
|
|
2325
|
+
* @example
|
|
2326
|
+
* ```ts
|
|
2327
|
+
* import { invertLeft } from '@src/core'
|
|
2328
|
+
*
|
|
2329
|
+
* invertLeft('add', 10, 3) // 7 — x + 3 = 10
|
|
2330
|
+
* invertLeft('multiply', 10, 0) // NaN — x * 0 = 10 has no solution
|
|
2331
|
+
* ```
|
|
2332
|
+
*/
|
|
2333
|
+
export declare function invertLeft(operator: MathOperation, value: number, rightValue: number): number;
|
|
2334
|
+
|
|
2335
|
+
/**
|
|
2336
|
+
* Invert a `left op x = value` step, solving for the RIGHT operand `x`.
|
|
2337
|
+
*
|
|
2338
|
+
* @remarks
|
|
2339
|
+
* The right-operand inverse of the symbolic reasoner's isolation: `add` inverts
|
|
2340
|
+
* to `value - left`, `subtract` to `left - value`, `multiply` to `value / left`
|
|
2341
|
+
* (with a zero `left` yielding `NaN`), `divide` to `left / value` (with a zero
|
|
2342
|
+
* `value` yielding `NaN`). Inversion by zero yields `NaN`, never a throw; a
|
|
2343
|
+
* non-invertible operator throws (caught per equation upstream).
|
|
2344
|
+
*
|
|
2345
|
+
* @param operator - The math operation to invert
|
|
2346
|
+
* @param value - The known result of `left op x`
|
|
2347
|
+
* @param leftValue - The known left operand
|
|
2348
|
+
* @returns The isolated right operand (`NaN` on a zero-division inverse)
|
|
2349
|
+
*
|
|
2350
|
+
* @example
|
|
2351
|
+
* ```ts
|
|
2352
|
+
* import { invertRight } from '@src/core'
|
|
2353
|
+
*
|
|
2354
|
+
* invertRight('subtract', 4, 10) // 6 — 10 - x = 4
|
|
2355
|
+
* invertRight('divide', 0, 10) // NaN — 10 / x = 0 has no finite solution
|
|
2356
|
+
* ```
|
|
2357
|
+
*/
|
|
2358
|
+
export declare function invertRight(operator: MathOperation, value: number, leftValue: number): number;
|
|
2359
|
+
|
|
2360
|
+
/**
|
|
2361
|
+
* Determine whether a value is an {@link Aggregation} literal.
|
|
2362
|
+
*
|
|
2363
|
+
* @param value - The value to test
|
|
2364
|
+
* @returns `true` when `value` is one of the five aggregations
|
|
2365
|
+
*
|
|
2366
|
+
* @example
|
|
2367
|
+
* ```ts
|
|
2368
|
+
* import { isAggregation } from '@src/core'
|
|
2369
|
+
*
|
|
2370
|
+
* isAggregation('sum') // true
|
|
2371
|
+
* isAggregation('median') // false
|
|
2372
|
+
* ```
|
|
2373
|
+
*/
|
|
2374
|
+
export declare const isAggregation: Guard<Aggregation>;
|
|
2375
|
+
|
|
2376
|
+
/**
|
|
2377
|
+
* Determine whether a value is a {@link Bounds} — an inclusive numeric clamp.
|
|
2378
|
+
*
|
|
2379
|
+
* @param value - The value to test
|
|
2380
|
+
* @returns `true` when `value` is a well-formed bounds record (both sides optional)
|
|
2381
|
+
*
|
|
2382
|
+
* @example
|
|
2383
|
+
* ```ts
|
|
2384
|
+
* import { isBounds } from '@src/core'
|
|
2385
|
+
*
|
|
2386
|
+
* isBounds({ minimum: 0, maximum: 100 }) // true
|
|
2387
|
+
* isBounds({}) // true — unbounded
|
|
2388
|
+
* isBounds({ minimum: NaN }) // false — non-finite
|
|
2389
|
+
* ```
|
|
2390
|
+
*/
|
|
2391
|
+
export declare function isBounds(value: unknown): value is Bounds;
|
|
2392
|
+
|
|
2393
|
+
/**
|
|
2394
|
+
* Determine whether a value is a {@link ChainingStrategy} literal.
|
|
2395
|
+
*
|
|
2396
|
+
* @param value - The value to test
|
|
2397
|
+
* @returns `true` when `value` is `'forward'` or `'backward'`
|
|
2398
|
+
*
|
|
2399
|
+
* @example
|
|
2400
|
+
* ```ts
|
|
2401
|
+
* import { isChainingStrategy } from '@src/core'
|
|
2402
|
+
*
|
|
2403
|
+
* isChainingStrategy('forward') // true
|
|
2404
|
+
* isChainingStrategy('upward') // false
|
|
2405
|
+
* ```
|
|
2406
|
+
*/
|
|
2407
|
+
export declare const isChainingStrategy: Guard<ChainingStrategy>;
|
|
2408
|
+
|
|
2409
|
+
/**
|
|
2410
|
+
* Determine whether a value is a {@link Check} — a field / operator / value
|
|
2411
|
+
* predicate.
|
|
2412
|
+
*
|
|
2413
|
+
* @remarks
|
|
2414
|
+
* `value` may be ANYTHING (including `null` / `undefined`) but the key must be
|
|
2415
|
+
* PRESENT — exact-record semantics reject a check that lost its `value` key.
|
|
2416
|
+
*
|
|
2417
|
+
* @param value - The value to test
|
|
2418
|
+
* @returns `true` when `value` is a well-formed check
|
|
2419
|
+
*
|
|
2420
|
+
* @example
|
|
2421
|
+
* ```ts
|
|
2422
|
+
* import { isCheck } from '@src/core'
|
|
2423
|
+
*
|
|
2424
|
+
* isCheck({ field: 'age', operator: 'above', value: 18 }) // true
|
|
2425
|
+
* isCheck({ field: 'age', operator: 'over', value: 18 }) // false — unknown operator
|
|
2426
|
+
* ```
|
|
2427
|
+
*/
|
|
2428
|
+
export declare function isCheck(value: unknown): value is Check;
|
|
2429
|
+
|
|
2430
|
+
/**
|
|
2431
|
+
* Determine whether a value is a {@link Comparison} literal.
|
|
2432
|
+
*
|
|
2433
|
+
* @param value - The value to test
|
|
2434
|
+
* @returns `true` when `value` is one of the ten comparison operators
|
|
2435
|
+
*
|
|
2436
|
+
* @example
|
|
2437
|
+
* ```ts
|
|
2438
|
+
* import { isComparison } from '@src/core'
|
|
2439
|
+
*
|
|
2440
|
+
* isComparison('between') // true
|
|
2441
|
+
* isComparison('greaterThan') // false — the operator vocabulary is single-word
|
|
2442
|
+
* ```
|
|
2443
|
+
*/
|
|
2444
|
+
export declare const isComparison: Guard<Comparison>;
|
|
2445
|
+
|
|
2446
|
+
/**
|
|
2447
|
+
* Determine whether a value is a {@link Definition} — any of the four
|
|
2448
|
+
* definition shapes, discriminated by `reasoning`.
|
|
2449
|
+
*
|
|
2450
|
+
* @param value - The value to test
|
|
2451
|
+
* @returns `true` when `value` is a well-formed definition of any reasoning
|
|
2452
|
+
*
|
|
2453
|
+
* @example
|
|
2454
|
+
* ```ts
|
|
2455
|
+
* import { isDefinition, logicalDefinition } from '@src/core'
|
|
2456
|
+
*
|
|
2457
|
+
* isDefinition(logicalDefinition('eligibility', 'Eligibility', [])) // true
|
|
2458
|
+
* isDefinition({ reasoning: 'quantum' }) // false
|
|
2459
|
+
* ```
|
|
2460
|
+
*/
|
|
2461
|
+
export declare function isDefinition(value: unknown): value is Definition;
|
|
2462
|
+
|
|
2463
|
+
/**
|
|
2464
|
+
* Determine whether a value is a `DefinitionBuilder` ENTITY — the brand-guarded
|
|
2465
|
+
* stateful workspace, not the plain {@link Definition} data union.
|
|
2466
|
+
*
|
|
2467
|
+
* @remarks
|
|
2468
|
+
* A `unique symbol` brand check (`Reflect.get`, AGENTS §14): a plain subject
|
|
2469
|
+
* is an open record whose values may legally be functions, so a
|
|
2470
|
+
* method-presence check (`typeof value.build === 'function'`) is FORGEABLE —
|
|
2471
|
+
* this guard is not. A module-owned `unique symbol` cannot be produced by
|
|
2472
|
+
* `JSON.parse` or written by any consumer that does not import
|
|
2473
|
+
* `DEFINITION_BUILDER_BRAND`, so plain data can never forge it. Total: a
|
|
2474
|
+
* non-object, a missing brand, or a hostile prototype all return `false`,
|
|
2475
|
+
* never throw.
|
|
2476
|
+
*
|
|
2477
|
+
* @param value - The value to test
|
|
2478
|
+
* @returns `true` when `value` carries the `DefinitionBuilder` entity brand
|
|
2479
|
+
*
|
|
2480
|
+
* @example
|
|
2481
|
+
* ```ts
|
|
2482
|
+
* import { createDefinitionBuilder, isDefinitionBuilder, quantitativeDefinition } from '@src/core'
|
|
2483
|
+
*
|
|
2484
|
+
* const definition = createDefinitionBuilder(quantitativeDefinition('risk', 'Risk', []))
|
|
2485
|
+
* isDefinitionBuilder(definition) // true
|
|
2486
|
+
* isDefinitionBuilder({ build: () => undefined }) // false — forged build field
|
|
2487
|
+
* isDefinitionBuilder(quantitativeDefinition('r', 'R', [])) // false — plain data, not the entity
|
|
2488
|
+
* ```
|
|
2489
|
+
*/
|
|
2490
|
+
export declare function isDefinitionBuilder(value: unknown): value is DefinitionBuilderInterface;
|
|
2491
|
+
|
|
2492
|
+
/**
|
|
2493
|
+
* Determine whether a value is an {@link Equation} — `left = right`, solved for
|
|
2494
|
+
* `target`.
|
|
2495
|
+
*
|
|
2496
|
+
* @param value - The value to test
|
|
2497
|
+
* @returns `true` when `value` is a well-formed equation
|
|
2498
|
+
*
|
|
2499
|
+
* @example
|
|
2500
|
+
* ```ts
|
|
2501
|
+
* import { constant, equation, isEquation, variable } from '@src/core'
|
|
2502
|
+
*
|
|
2503
|
+
* isEquation(equation('e1', variable('x'), constant(42), 'x')) // true
|
|
2504
|
+
* isEquation({ id: 'e1', target: 'x' }) // false — sides missing
|
|
2505
|
+
* ```
|
|
2506
|
+
*/
|
|
2507
|
+
export declare function isEquation(value: unknown): value is Equation;
|
|
2508
|
+
|
|
2509
|
+
/**
|
|
2510
|
+
* Determine whether a value is an {@link Expression} — a boolean expression
|
|
2511
|
+
* tree of atoms and compounds, discriminated by `form`.
|
|
2512
|
+
*
|
|
2513
|
+
* @remarks
|
|
2514
|
+
* Recursive through `lazyOf` (AGENTS §14) — recursion is STACK-BOUNDED, not
|
|
2515
|
+
* unbounded: nesting beyond the engine's stack budget (roughly 1000 levels)
|
|
2516
|
+
* and cyclic input are CONTAINED as `false`, never a throw. Input past that
|
|
2517
|
+
* bound is rejected, not validated.
|
|
2518
|
+
*
|
|
2519
|
+
* @param value - The value to test
|
|
2520
|
+
* @returns `true` when `value` is a well-formed expression tree
|
|
2521
|
+
*
|
|
2522
|
+
* @example
|
|
2523
|
+
* ```ts
|
|
2524
|
+
* import { atom, compound, isExpression } from '@src/core'
|
|
2525
|
+
*
|
|
2526
|
+
* isExpression(atom('age', 'from', 18)) // true
|
|
2527
|
+
* isExpression(compound('and', [atom('age', 'from', 18)])) // true
|
|
2528
|
+
* isExpression({ form: 'compound', operator: 'and' }) // false — operands missing
|
|
2529
|
+
* ```
|
|
2530
|
+
*/
|
|
2531
|
+
export declare function isExpression(value: unknown): value is Expression;
|
|
2532
|
+
|
|
2533
|
+
/**
|
|
2534
|
+
* Determine whether a value is a {@link Fact} — a predicate over positional
|
|
2535
|
+
* terms.
|
|
2536
|
+
*
|
|
2537
|
+
* @remarks
|
|
2538
|
+
* `terms` elements are unconstrained (`unknown`) — a `'?'`-prefixed string term
|
|
2539
|
+
* is a unification variable, anything else a constant.
|
|
2540
|
+
*
|
|
2541
|
+
* @param value - The value to test
|
|
2542
|
+
* @returns `true` when `value` is a well-formed fact
|
|
2543
|
+
*
|
|
2544
|
+
* @example
|
|
2545
|
+
* ```ts
|
|
2546
|
+
* import { isFact } from '@src/core'
|
|
2547
|
+
*
|
|
2548
|
+
* isFact({ id: 'f1', predicate: 'human', terms: ['socrates'] }) // true
|
|
2549
|
+
* isFact({ id: 'f1', predicate: 'human' }) // false — terms missing
|
|
2550
|
+
* ```
|
|
2551
|
+
*/
|
|
2552
|
+
export declare function isFact(value: unknown): value is Fact;
|
|
2553
|
+
|
|
2554
|
+
/**
|
|
2555
|
+
* Determine whether a value is a {@link Factor} — one scored input of a
|
|
2556
|
+
* quantitative group.
|
|
2557
|
+
*
|
|
2558
|
+
* @param value - The value to test
|
|
2559
|
+
* @returns `true` when `value` is a well-formed factor
|
|
2560
|
+
*
|
|
2561
|
+
* @example
|
|
2562
|
+
* ```ts
|
|
2563
|
+
* import { isFactor } from '@src/core'
|
|
2564
|
+
*
|
|
2565
|
+
* isFactor({ id: 'age', name: 'Age', source: { origin: 'field', field: 'age' } }) // true
|
|
2566
|
+
* isFactor({ id: 'age', name: 'Age' }) // false — no source
|
|
2567
|
+
* ```
|
|
2568
|
+
*/
|
|
2569
|
+
export declare function isFactor(value: unknown): value is Factor;
|
|
2570
|
+
|
|
2571
|
+
/**
|
|
2572
|
+
* Determine whether a value is a {@link FactorGroup} — a group of factors
|
|
2573
|
+
* aggregated into one value.
|
|
2574
|
+
*
|
|
2575
|
+
* @param value - The value to test
|
|
2576
|
+
* @returns `true` when `value` is a well-formed factor group
|
|
2577
|
+
*
|
|
2578
|
+
* @example
|
|
2579
|
+
* ```ts
|
|
2580
|
+
* import { isFactorGroup, staticFactor } from '@src/core'
|
|
2581
|
+
*
|
|
2582
|
+
* isFactorGroup({ id: 'g1', name: 'g1', aggregation: 'sum', factors: [staticFactor('f1', 10)] }) // true
|
|
2583
|
+
* isFactorGroup({ id: 'g1', name: 'g1', aggregation: 'median', factors: [] }) // false
|
|
2584
|
+
* ```
|
|
2585
|
+
*/
|
|
2586
|
+
export declare function isFactorGroup(value: unknown): value is FactorGroup;
|
|
2587
|
+
|
|
2588
|
+
/**
|
|
2589
|
+
* Determine whether a value is a {@link FactorRange} — one band of a range
|
|
2590
|
+
* source.
|
|
2591
|
+
*
|
|
2592
|
+
* @param value - The value to test
|
|
2593
|
+
* @returns `true` when `value` is a well-formed factor range
|
|
2594
|
+
*
|
|
2595
|
+
* @example
|
|
2596
|
+
* ```ts
|
|
2597
|
+
* import { isFactorRange } from '@src/core'
|
|
2598
|
+
*
|
|
2599
|
+
* isFactorRange({ bounds: { maximum: 25 }, value: 1.5 }) // true
|
|
2600
|
+
* isFactorRange({ value: 42 }) // true — a catch-all band
|
|
2601
|
+
* ```
|
|
2602
|
+
*/
|
|
2603
|
+
export declare function isFactorRange(value: unknown): value is FactorRange;
|
|
2604
|
+
|
|
2605
|
+
/**
|
|
2606
|
+
* Determine whether a value is a {@link FieldPath} — a single string key or an
|
|
2607
|
+
* array of keys descending into nested objects.
|
|
2608
|
+
*
|
|
2609
|
+
* @param value - The value to test
|
|
2610
|
+
* @returns `true` when `value` is a string or an array of strings
|
|
2611
|
+
*
|
|
2612
|
+
* @example
|
|
2613
|
+
* ```ts
|
|
2614
|
+
* import { isFieldPath } from '@src/core'
|
|
2615
|
+
*
|
|
2616
|
+
* isFieldPath('age') // true — ONE key (never dot-split)
|
|
2617
|
+
* isFieldPath(['address', 'city']) // true — descends
|
|
2618
|
+
* isFieldPath(42) // false
|
|
2619
|
+
* ```
|
|
2620
|
+
*/
|
|
2621
|
+
export declare const isFieldPath: Guard<FieldPath>;
|
|
2622
|
+
|
|
2623
|
+
/**
|
|
2624
|
+
* Determine whether a value is an {@link Inference} — premise patterns and a
|
|
2625
|
+
* conclusion pattern.
|
|
2626
|
+
*
|
|
2627
|
+
* @param value - The value to test
|
|
2628
|
+
* @returns `true` when `value` is a well-formed inference
|
|
2629
|
+
*
|
|
2630
|
+
* @example
|
|
2631
|
+
* ```ts
|
|
2632
|
+
* import { fact, inference, isInference } from '@src/core'
|
|
2633
|
+
*
|
|
2634
|
+
* isInference(inference('mortal', [fact('p1', 'human', ['?x'])], fact('c1', 'mortal', ['?x']))) // true
|
|
2635
|
+
* isInference({ id: 'mortal' }) // false
|
|
2636
|
+
* ```
|
|
2637
|
+
*/
|
|
2638
|
+
export declare function isInference(value: unknown): value is Inference;
|
|
2639
|
+
|
|
2640
|
+
/**
|
|
2641
|
+
* Determine whether a value is an {@link InferentialDefinition}.
|
|
2642
|
+
*
|
|
2643
|
+
* @param value - The value to test
|
|
2644
|
+
* @returns `true` when `value` is a well-formed inferential definition
|
|
2645
|
+
*
|
|
2646
|
+
* @example
|
|
2647
|
+
* ```ts
|
|
2648
|
+
* import { inferentialDefinition, isInferentialDefinition } from '@src/core'
|
|
2649
|
+
*
|
|
2650
|
+
* isInferentialDefinition(inferentialDefinition('birds', 'Birds', [], [])) // true
|
|
2651
|
+
* isInferentialDefinition({ reasoning: 'inferential', id: 'birds' }) // false
|
|
2652
|
+
* ```
|
|
2653
|
+
*/
|
|
2654
|
+
export declare function isInferentialDefinition(value: unknown): value is InferentialDefinition;
|
|
2655
|
+
|
|
2656
|
+
/**
|
|
2657
|
+
* Determine whether a value is a {@link LogicalDefinition}.
|
|
2658
|
+
*
|
|
2659
|
+
* @param value - The value to test
|
|
2660
|
+
* @returns `true` when `value` is a well-formed logical definition
|
|
2661
|
+
*
|
|
2662
|
+
* @example
|
|
2663
|
+
* ```ts
|
|
2664
|
+
* import { isLogicalDefinition, logicalDefinition } from '@src/core'
|
|
2665
|
+
*
|
|
2666
|
+
* isLogicalDefinition(logicalDefinition('eligibility', 'Eligibility', [])) // true
|
|
2667
|
+
* isLogicalDefinition({ reasoning: 'logical', id: 'eligibility' }) // false
|
|
2668
|
+
* ```
|
|
2669
|
+
*/
|
|
2670
|
+
export declare function isLogicalDefinition(value: unknown): value is LogicalDefinition;
|
|
2671
|
+
|
|
2672
|
+
/**
|
|
2673
|
+
* Determine whether a value is a {@link LogicalOperator} literal.
|
|
2674
|
+
*
|
|
2675
|
+
* @param value - The value to test
|
|
2676
|
+
* @returns `true` when `value` is one of the five logical connectives
|
|
2677
|
+
*
|
|
2678
|
+
* @example
|
|
2679
|
+
* ```ts
|
|
2680
|
+
* import { isLogicalOperator } from '@src/core'
|
|
2681
|
+
*
|
|
2682
|
+
* isLogicalOperator('implies') // true
|
|
2683
|
+
* isLogicalOperator('nand') // false
|
|
2684
|
+
* ```
|
|
2685
|
+
*/
|
|
2686
|
+
export declare const isLogicalOperator: Guard<LogicalOperator>;
|
|
2687
|
+
|
|
2688
|
+
/**
|
|
2689
|
+
* Determine whether a value is a {@link MathOperation} literal.
|
|
2690
|
+
*
|
|
2691
|
+
* @param value - The value to test
|
|
2692
|
+
* @returns `true` when `value` is one of the thirteen math operations
|
|
2693
|
+
*
|
|
2694
|
+
* @example
|
|
2695
|
+
* ```ts
|
|
2696
|
+
* import { isMathOperation } from '@src/core'
|
|
2697
|
+
*
|
|
2698
|
+
* isMathOperation('multiply') // true
|
|
2699
|
+
* isMathOperation('modulo') // false
|
|
2700
|
+
* ```
|
|
2701
|
+
*/
|
|
2702
|
+
export declare const isMathOperation: Guard<MathOperation>;
|
|
2703
|
+
|
|
2704
|
+
/**
|
|
2705
|
+
* Determine whether a value is a record whose every value is a finite number —
|
|
2706
|
+
* the shape of a `LookupSource.table` and a `SymbolicDefinition.variables`.
|
|
2707
|
+
*
|
|
2708
|
+
* @param value - The value to test
|
|
2709
|
+
* @returns `true` when `value` is a plain record of finite numbers
|
|
2710
|
+
*
|
|
2711
|
+
* @example
|
|
2712
|
+
* ```ts
|
|
2713
|
+
* import { isNumberRecord } from '@src/core'
|
|
2714
|
+
*
|
|
2715
|
+
* isNumberRecord({ CA: 1.2, NY: 0.8 }) // true
|
|
2716
|
+
* isNumberRecord({ CA: '1.2' }) // false — strings do not coerce here
|
|
2717
|
+
* ```
|
|
2718
|
+
*/
|
|
2719
|
+
export declare const isNumberRecord: Guard<Readonly<Record<string, number>>>;
|
|
2720
|
+
|
|
2721
|
+
/**
|
|
2722
|
+
* Determine whether a value is a {@link QuantitativeDefinition}.
|
|
2723
|
+
*
|
|
2724
|
+
* @param value - The value to test
|
|
2725
|
+
* @returns `true` when `value` is a well-formed quantitative definition
|
|
2726
|
+
*
|
|
2727
|
+
* @example
|
|
2728
|
+
* ```ts
|
|
2729
|
+
* import { isQuantitativeDefinition, quantitativeDefinition } from '@src/core'
|
|
2730
|
+
*
|
|
2731
|
+
* isQuantitativeDefinition(quantitativeDefinition('risk', 'Risk', [])) // true
|
|
2732
|
+
* isQuantitativeDefinition({ reasoning: 'quantitative', id: 'risk' }) // false
|
|
2733
|
+
* ```
|
|
2734
|
+
*/
|
|
2735
|
+
export declare function isQuantitativeDefinition(value: unknown): value is QuantitativeDefinition;
|
|
2736
|
+
|
|
2737
|
+
/**
|
|
2738
|
+
* Narrow an unknown caught value to a {@link ReasonError}.
|
|
2739
|
+
*
|
|
2740
|
+
* @param value - The value to test (typically a `catch` binding)
|
|
2741
|
+
* @returns `true` when `value` is a {@link ReasonError}
|
|
2742
|
+
*
|
|
2743
|
+
* @example
|
|
2744
|
+
* ```ts
|
|
2745
|
+
* try {
|
|
2746
|
+
* reason.reason(subject, definition)
|
|
2747
|
+
* } catch (error) {
|
|
2748
|
+
* if (isReasonError(error) && error.code === 'MISSING') registerFallback()
|
|
2749
|
+
* }
|
|
2750
|
+
* ```
|
|
2751
|
+
*/
|
|
2752
|
+
export declare function isReasonError(value: unknown): value is ReasonError;
|
|
2753
|
+
|
|
2754
|
+
/**
|
|
2755
|
+
* Determine whether a value is a {@link Reasoning} literal.
|
|
2756
|
+
*
|
|
2757
|
+
* @param value - The value to test
|
|
2758
|
+
* @returns `true` when `value` is one of the four reasoning strategies
|
|
2759
|
+
*
|
|
2760
|
+
* @example
|
|
2761
|
+
* ```ts
|
|
2762
|
+
* import { isReasoning } from '@src/core'
|
|
2763
|
+
*
|
|
2764
|
+
* isReasoning('logical') // true
|
|
2765
|
+
* isReasoning('fuzzy') // false
|
|
2766
|
+
* ```
|
|
2767
|
+
*/
|
|
2768
|
+
export declare const isReasoning: Guard<Reasoning>;
|
|
2769
|
+
|
|
2770
|
+
/**
|
|
2771
|
+
* Determine whether a value is a {@link Rule} — premises and a conclusion.
|
|
2772
|
+
*
|
|
2773
|
+
* @param value - The value to test
|
|
2774
|
+
* @returns `true` when `value` is a well-formed rule
|
|
2775
|
+
*
|
|
2776
|
+
* @example
|
|
2777
|
+
* ```ts
|
|
2778
|
+
* import { atom, isRule, rule } from '@src/core'
|
|
2779
|
+
*
|
|
2780
|
+
* isRule(rule('adult', [atom('age', 'from', 18)], atom('adult', 'equals', true))) // true
|
|
2781
|
+
* isRule({ id: 'adult' }) // false
|
|
2782
|
+
* ```
|
|
2783
|
+
*/
|
|
2784
|
+
export declare function isRule(value: unknown): value is Rule;
|
|
2785
|
+
|
|
2786
|
+
/**
|
|
2787
|
+
* Determine whether a value is a {@link Source} — any of the four factor
|
|
2788
|
+
* sources, discriminated by `origin`.
|
|
2789
|
+
*
|
|
2790
|
+
* @param value - The value to test
|
|
2791
|
+
* @returns `true` when `value` is a well-formed static / field / lookup / range source
|
|
2792
|
+
*
|
|
2793
|
+
* @example
|
|
2794
|
+
* ```ts
|
|
2795
|
+
* import { isSource } from '@src/core'
|
|
2796
|
+
*
|
|
2797
|
+
* isSource({ origin: 'static', value: 42 }) // true
|
|
2798
|
+
* isSource({ origin: 'lookup', field: 'state', table: { CA: 5 } }) // true
|
|
2799
|
+
* isSource({ origin: 'random' }) // false
|
|
2800
|
+
* ```
|
|
2801
|
+
*/
|
|
2802
|
+
export declare function isSource(value: unknown): value is Source;
|
|
2803
|
+
|
|
2804
|
+
/**
|
|
2805
|
+
* Determine whether a value is a `Subject` — a plain record of fields to reason
|
|
2806
|
+
* about.
|
|
2807
|
+
*
|
|
2808
|
+
* @param value - The value to test
|
|
2809
|
+
* @returns `true` when `value` is a plain record (arrays and class instances fail)
|
|
2810
|
+
*
|
|
2811
|
+
* @example
|
|
2812
|
+
* ```ts
|
|
2813
|
+
* import { isSubject } from '@src/core'
|
|
2814
|
+
*
|
|
2815
|
+
* isSubject({ age: 30 }) // true
|
|
2816
|
+
* isSubject([1, 2, 3]) // false — an array is not a subject
|
|
2817
|
+
* ```
|
|
2818
|
+
*/
|
|
2819
|
+
export declare const isSubject: Guard<Readonly<Record<string, unknown>>>;
|
|
2820
|
+
|
|
2821
|
+
/**
|
|
2822
|
+
* Determine whether a value is a `SubjectBuilder` ENTITY — the brand-guarded
|
|
2823
|
+
* stateful workspace, not the plain {@link Subject} data record.
|
|
2824
|
+
*
|
|
2825
|
+
* @remarks
|
|
2826
|
+
* A `unique symbol` brand check (`Reflect.get`, AGENTS §14), distinct from
|
|
2827
|
+
* {@link isDefinitionBuilder} — the two entities can never match each other's
|
|
2828
|
+
* guard. Total: a non-object, a missing brand, or a hostile prototype all
|
|
2829
|
+
* return `false`, never throw.
|
|
2830
|
+
*
|
|
2831
|
+
* @param value - The value to test
|
|
2832
|
+
* @returns `true` when `value` carries the `SubjectBuilder` entity brand
|
|
2833
|
+
*
|
|
2834
|
+
* @example
|
|
2835
|
+
* ```ts
|
|
2836
|
+
* import { createSubjectBuilder, isSubjectBuilder } from '@src/core'
|
|
2837
|
+
*
|
|
2838
|
+
* const subject = createSubjectBuilder({ id: 's1', age: 30 })
|
|
2839
|
+
* isSubjectBuilder(subject) // true
|
|
2840
|
+
* isSubjectBuilder({ build: () => ({}) }) // false — forged build field
|
|
2841
|
+
* isSubjectBuilder({ id: 's1', age: 30 }) // false — plain data, not the entity
|
|
2842
|
+
* ```
|
|
2843
|
+
*/
|
|
2844
|
+
export declare function isSubjectBuilder(value: unknown): value is SubjectBuilderInterface;
|
|
2845
|
+
|
|
2846
|
+
/**
|
|
2847
|
+
* Determine whether a value is a {@link SymbolicDefinition}.
|
|
2848
|
+
*
|
|
2849
|
+
* @param value - The value to test
|
|
2850
|
+
* @returns `true` when `value` is a well-formed symbolic definition
|
|
2851
|
+
*
|
|
2852
|
+
* @example
|
|
2853
|
+
* ```ts
|
|
2854
|
+
* import { isSymbolicDefinition, symbolicDefinition } from '@src/core'
|
|
2855
|
+
*
|
|
2856
|
+
* isSymbolicDefinition(symbolicDefinition('rate', 'Rate', [])) // true
|
|
2857
|
+
* isSymbolicDefinition({ reasoning: 'symbolic', id: 'rate' }) // false
|
|
2858
|
+
* ```
|
|
2859
|
+
*/
|
|
2860
|
+
export declare function isSymbolicDefinition(value: unknown): value is SymbolicDefinition;
|
|
2861
|
+
|
|
2862
|
+
/**
|
|
2863
|
+
* Determine whether a value is a {@link SymbolicExpression} — an algebraic
|
|
2864
|
+
* expression tree of variables, constants, and operations, discriminated by
|
|
2865
|
+
* `form`.
|
|
2866
|
+
*
|
|
2867
|
+
* @remarks
|
|
2868
|
+
* Recursive through `lazyOf` (AGENTS §14) — recursion is STACK-BOUNDED, not
|
|
2869
|
+
* unbounded: nesting beyond the engine's stack budget (roughly 1000 levels)
|
|
2870
|
+
* and cyclic input are CONTAINED as `false`, never a throw. Input past that
|
|
2871
|
+
* bound is rejected, not validated.
|
|
2872
|
+
*
|
|
2873
|
+
* @param value - The value to test
|
|
2874
|
+
* @returns `true` when `value` is a well-formed symbolic expression tree
|
|
2875
|
+
*
|
|
2876
|
+
* @example
|
|
2877
|
+
* ```ts
|
|
2878
|
+
* import { constant, isSymbolicExpression, operation, variable } from '@src/core'
|
|
2879
|
+
*
|
|
2880
|
+
* isSymbolicExpression(operation('add', variable('x'), constant(1))) // true
|
|
2881
|
+
* isSymbolicExpression({ form: 'variable' }) // false — name missing
|
|
2882
|
+
* ```
|
|
2883
|
+
*/
|
|
2884
|
+
export declare function isSymbolicExpression(value: unknown): value is SymbolicExpression;
|
|
2885
|
+
|
|
2886
|
+
/**
|
|
2887
|
+
* Determine whether a value is a {@link Transform} — one math step.
|
|
2888
|
+
*
|
|
2889
|
+
* @param value - The value to test
|
|
2890
|
+
* @returns `true` when `value` is a well-formed transform
|
|
2891
|
+
*
|
|
2892
|
+
* @example
|
|
2893
|
+
* ```ts
|
|
2894
|
+
* import { isTransform } from '@src/core'
|
|
2895
|
+
*
|
|
2896
|
+
* isTransform({ operation: 'multiply', operand: 2 }) // true
|
|
2897
|
+
* isTransform({ operation: 'round' }) // true — operand is optional
|
|
2898
|
+
* isTransform({ operation: 'multiply', by: 2 }) // false — extra key
|
|
2899
|
+
* ```
|
|
2900
|
+
*/
|
|
2901
|
+
export declare function isTransform(value: unknown): value is Transform;
|
|
2902
|
+
|
|
2903
|
+
/** Default `id` for a `LogicalReasoner`. */
|
|
2904
|
+
export declare const LOGICAL_ID = "logical";
|
|
2905
|
+
|
|
2906
|
+
/**
|
|
2907
|
+
* A logical (rule-based deduction) definition.
|
|
2908
|
+
*
|
|
2909
|
+
* @remarks
|
|
2910
|
+
* `strategy` picks forward fixpoint chaining or backward goal-driven proving;
|
|
2911
|
+
* `depth` caps forward iterations / backward recursion (default `10`).
|
|
2912
|
+
*/
|
|
2913
|
+
export declare interface LogicalDefinition {
|
|
2914
|
+
readonly reasoning: 'logical';
|
|
2915
|
+
readonly id: string;
|
|
2916
|
+
readonly name: string;
|
|
2917
|
+
readonly description?: string;
|
|
2918
|
+
readonly rules: readonly Rule[];
|
|
2919
|
+
readonly strategy: ChainingStrategy;
|
|
2920
|
+
readonly depth?: number;
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
/**
|
|
2924
|
+
* Build a {@link LogicalDefinition}.
|
|
2925
|
+
*
|
|
2926
|
+
* @remarks
|
|
2927
|
+
* `strategy` defaults to `'forward'`; set `strategy`, `description`, or `depth`
|
|
2928
|
+
* through `overrides`.
|
|
2929
|
+
*
|
|
2930
|
+
* @param id - The definition id
|
|
2931
|
+
* @param name - The display name
|
|
2932
|
+
* @param rules - The deduction rules
|
|
2933
|
+
* @param overrides - Optional {@link LogicalDefinition} fields merged over the defaults
|
|
2934
|
+
* @returns A fresh logical definition
|
|
2935
|
+
*
|
|
2936
|
+
* @example
|
|
2937
|
+
* ```ts
|
|
2938
|
+
* import { atom, logicalDefinition, rule } from '@src/core'
|
|
2939
|
+
*
|
|
2940
|
+
* logicalDefinition('eligibility', 'Eligibility', [
|
|
2941
|
+
* rule('adult', [atom('age', 'from', 18)], atom('adult', 'equals', true)),
|
|
2942
|
+
* ])
|
|
2943
|
+
* ```
|
|
2944
|
+
*/
|
|
2945
|
+
export declare function logicalDefinition(id: string, name: string, rules: readonly Rule[], overrides?: Partial<Omit<LogicalDefinition, 'reasoning' | 'id' | 'name' | 'rules'>>): LogicalDefinition;
|
|
2946
|
+
|
|
2947
|
+
/**
|
|
2948
|
+
* A logical connective inside a compound {@link Expression}.
|
|
2949
|
+
*
|
|
2950
|
+
* @remarks
|
|
2951
|
+
* `not` reads only its first operand (empty operands are vacuously true);
|
|
2952
|
+
* `implies` and `xor` read their first two (`implies` is vacuously true below
|
|
2953
|
+
* two operands, `xor` is false).
|
|
2954
|
+
*/
|
|
2955
|
+
export declare type LogicalOperator = 'and' | 'or' | 'not' | 'implies' | 'xor';
|
|
2956
|
+
|
|
2957
|
+
/**
|
|
2958
|
+
* The logical reasoner — rule-based boolean deduction with forward or backward
|
|
2959
|
+
* chaining.
|
|
2960
|
+
*
|
|
2961
|
+
* @remarks
|
|
2962
|
+
* Forward chaining is a naive fixpoint capped at `depth` iterations: rules run
|
|
2963
|
+
* in ascending `priority` order each pass, and a firing rule's conclusion atoms
|
|
2964
|
+
* become a derived overlay that SHADOWS same-named subject fields on later
|
|
2965
|
+
* passes (derived keys are `formatField(check.field)` strings). Convergence —
|
|
2966
|
+
* one full pass with no new derivation — appends a trace containing
|
|
2967
|
+
* `converged`. Final rule results re-evaluate against the settled overlay in
|
|
2968
|
+
* ORIGINAL rule order (forward) / priority-sorted order (backward), and the
|
|
2969
|
+
* overall `conclusion` is the LAST result's conclusion. Backward chaining
|
|
2970
|
+
* proves EVERY rule goal-first: an unmet premise triggers sub-goal search
|
|
2971
|
+
* through rules whose conclusion atoms assert the needed `field = value` pair,
|
|
2972
|
+
* guarded by a visited-rule set (cycle-safe) plus the depth cap; `not` succeeds
|
|
2973
|
+
* when its operand cannot be established (negation-as-failure) and `implies` is
|
|
2974
|
+
* vacuously true on an unprovable antecedent. Expression evaluation is EAGER
|
|
2975
|
+
* (no short-circuit); conclusion extraction IGNORES connectives — every atom
|
|
2976
|
+
* inside a conclusion is asserted, even under `not` / `or`. Overlay bookkeeping
|
|
2977
|
+
* compares with SameValueZero (`equalValues`), so a NaN-valued conclusion
|
|
2978
|
+
* derives once and the fixpoint converges. Nothing mutates its inputs; fully
|
|
2979
|
+
* deterministic (AGENTS §11).
|
|
2980
|
+
*/
|
|
2981
|
+
export declare class LogicalReasoner implements ReasonerInterface {
|
|
2982
|
+
#private;
|
|
2983
|
+
constructor(options?: LogicalReasonerOptions);
|
|
2984
|
+
get id(): string;
|
|
2985
|
+
get reasoning(): Reasoning;
|
|
2986
|
+
supports(definition: Definition): boolean;
|
|
2987
|
+
validate(definition: Definition): ReasonValidationResult;
|
|
2988
|
+
reason(subject: Subject, definition: Definition): ReasonResult;
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
/**
|
|
2992
|
+
* Options for `createLogicalReasoner` / the `LogicalReasoner` constructor.
|
|
2993
|
+
*
|
|
2994
|
+
* @remarks
|
|
2995
|
+
* `id` — the reasoner's identity string (defaults to `LOGICAL_ID`).
|
|
2996
|
+
* `evaluator` — the injectable check evaluator (defaults to a fresh
|
|
2997
|
+
* default-constructed instance).
|
|
2998
|
+
*/
|
|
2999
|
+
export declare interface LogicalReasonerOptions {
|
|
3000
|
+
readonly id?: string;
|
|
3001
|
+
readonly evaluator?: EvaluatorInterface;
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
/**
|
|
3005
|
+
* The outcome of logical reasoning.
|
|
3006
|
+
*
|
|
3007
|
+
* @remarks
|
|
3008
|
+
* `conclusion` is the LAST evaluated rule's conclusion (`false` when no rule
|
|
3009
|
+
* was evaluated); `count` tallies the applied rules; disabled rules are
|
|
3010
|
+
* omitted from `rules` entirely.
|
|
3011
|
+
*/
|
|
3012
|
+
export declare interface LogicalResult {
|
|
3013
|
+
readonly reasoning: 'logical';
|
|
3014
|
+
readonly conclusion: boolean;
|
|
3015
|
+
readonly rules: readonly RuleResult[];
|
|
3016
|
+
readonly count: number;
|
|
3017
|
+
readonly success: boolean;
|
|
3018
|
+
readonly trace: readonly string[];
|
|
3019
|
+
readonly errors: readonly string[];
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3022
|
+
/**
|
|
3023
|
+
* Build a {@link Factor} over a lookup {@link Source}.
|
|
3024
|
+
*
|
|
3025
|
+
* @param id - The factor id
|
|
3026
|
+
* @param field - The subject field to resolve (stringified into a table key)
|
|
3027
|
+
* @param table - The lookup table
|
|
3028
|
+
* @param overrides - Optional {@link Factor} fields merged over the defaults
|
|
3029
|
+
* @returns A fresh factor
|
|
3030
|
+
*
|
|
3031
|
+
* @example
|
|
3032
|
+
* ```ts
|
|
3033
|
+
* import { lookupFactor } from '@src/core'
|
|
3034
|
+
*
|
|
3035
|
+
* lookupFactor('state-score', 'state', { CA: 5, NY: 8 }, { fallback: 1 })
|
|
3036
|
+
* ```
|
|
3037
|
+
*/
|
|
3038
|
+
export declare function lookupFactor(id: string, field: FieldPath, table: Readonly<Record<string, number>>, overrides?: Partial<Omit<Factor, 'id' | 'source'>>): Factor;
|
|
3039
|
+
|
|
3040
|
+
/**
|
|
3041
|
+
* A factor source mapping a subject field through a lookup table.
|
|
3042
|
+
*
|
|
3043
|
+
* @remarks
|
|
3044
|
+
* A missing or `null` field takes the factor's `fallback` directly (never the
|
|
3045
|
+
* `''` table key); a PRESENT value is stringified into a `table` key (a numeric
|
|
3046
|
+
* `42` finds the key `'42'`, a real `''` value may hit a `''` key). Only OWN
|
|
3047
|
+
* table keys hit — an absent or inherited key falls back.
|
|
3048
|
+
*/
|
|
3049
|
+
export declare interface LookupSource {
|
|
3050
|
+
readonly origin: 'lookup';
|
|
3051
|
+
readonly field: FieldPath;
|
|
3052
|
+
readonly table: Readonly<Record<string, number>>;
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
/**
|
|
3056
|
+
* Build a lookup {@link Source} — a subject field mapped through a table.
|
|
3057
|
+
*
|
|
3058
|
+
* @param field - The subject field to resolve (stringified into a table key)
|
|
3059
|
+
* @param table - The lookup table
|
|
3060
|
+
* @returns A fresh lookup source
|
|
3061
|
+
*
|
|
3062
|
+
* @example
|
|
3063
|
+
* ```ts
|
|
3064
|
+
* import { lookupSource } from '@src/core'
|
|
3065
|
+
*
|
|
3066
|
+
* lookupSource('state', { CA: 5, NY: 8, TX: 2 })
|
|
3067
|
+
* ```
|
|
3068
|
+
*/
|
|
3069
|
+
export declare function lookupSource(field: FieldPath, table: Readonly<Record<string, number>>): Source;
|
|
3070
|
+
|
|
3071
|
+
/**
|
|
3072
|
+
* Positionally unify a pattern fact against a candidate fact — returning the
|
|
3073
|
+
* variable bindings on success, `undefined` on mismatch.
|
|
3074
|
+
*
|
|
3075
|
+
* @remarks
|
|
3076
|
+
* The bidirectional unification of the inferential reasoner: a `'?'`-prefixed
|
|
3077
|
+
* string term on EITHER side (pattern or candidate) is a variable that binds to
|
|
3078
|
+
* the opposite term (the `'?'` prefix is kept in the binding key), while
|
|
3079
|
+
* consistency is enforced within the match — a variable seen twice must bind the
|
|
3080
|
+
* SAME value (raw `!==`) or the whole match fails. A predicate mismatch or an
|
|
3081
|
+
* arity (term-count) mismatch fails immediately; non-variable terms must be
|
|
3082
|
+
* strictly (`===`) equal.
|
|
3083
|
+
*
|
|
3084
|
+
* @param pattern - The pattern fact (may carry `'?'` variables)
|
|
3085
|
+
* @param candidate - The candidate fact to unify against (may also carry `'?'` variables)
|
|
3086
|
+
* @returns A fresh bindings record, or `undefined` when they do not unify
|
|
3087
|
+
*
|
|
3088
|
+
* @example
|
|
3089
|
+
* ```ts
|
|
3090
|
+
* import { fact, matchFacts } from '@src/core'
|
|
3091
|
+
*
|
|
3092
|
+
* matchFacts(fact('p', 'parent', ['?x', 'bob']), fact('f', 'parent', ['alice', 'bob'])) // { '?x': 'alice' }
|
|
3093
|
+
* matchFacts(fact('p', 'parent', ['?x']), fact('f', 'human', ['x'])) // undefined — predicate
|
|
3094
|
+
* ```
|
|
3095
|
+
*/
|
|
3096
|
+
export declare function matchFacts(pattern: Fact, candidate: Fact): Record<string, unknown> | undefined;
|
|
3097
|
+
|
|
3098
|
+
/**
|
|
3099
|
+
* A math operation applied by the {@link TransformerInterface} and inside
|
|
3100
|
+
* {@link SymbolicExpression} trees.
|
|
3101
|
+
*
|
|
3102
|
+
* @remarks
|
|
3103
|
+
* `round` / `ceil` / `floor` / `abs` are unary — they ignore the operand /
|
|
3104
|
+
* right side. All others are binary.
|
|
3105
|
+
*/
|
|
3106
|
+
export declare type MathOperation = 'add' | 'subtract' | 'multiply' | 'divide' | 'percentage' | 'minimum' | 'maximum' | 'average' | 'power' | 'round' | 'ceil' | 'floor' | 'abs';
|
|
3107
|
+
|
|
3108
|
+
/**
|
|
3109
|
+
* Reconcile two id-keyed collections — an incoming-order upsert with
|
|
3110
|
+
* base-only survivors appended after.
|
|
3111
|
+
*
|
|
3112
|
+
* @remarks
|
|
3113
|
+
* The Strategic-Merge-Patch-style id-keyed upsert of PROPOSAL.md §6-§7:
|
|
3114
|
+
* the result is ordered by `incoming`'s id order FIRST (each element resolved
|
|
3115
|
+
* through `resolve` when its id also exists in `base`, defaulting to
|
|
3116
|
+
* incoming-wins-wholesale), THEN the `base`-only survivors in `base`'s own
|
|
3117
|
+
* order (retained, never deleted — merge is additive). Same-id twins within
|
|
3118
|
+
* EITHER input are deduped to their first occurrence.
|
|
3119
|
+
*
|
|
3120
|
+
* @typeParam T - An id-carrying element type
|
|
3121
|
+
* @param base - The base collection
|
|
3122
|
+
* @param incoming - The incoming collection (its order and matches take priority)
|
|
3123
|
+
* @param resolve - How to reconcile a matched (same-id) pair; defaults to keeping the incoming element wholesale
|
|
3124
|
+
* @returns A fresh, deduped, incoming-ordered-then-base-survivors array
|
|
3125
|
+
*
|
|
3126
|
+
* @example
|
|
3127
|
+
* ```ts
|
|
3128
|
+
* import { mergeById } from '@src/core'
|
|
3129
|
+
*
|
|
3130
|
+
* mergeById([{ id: 'a', v: 1 }, { id: 'b', v: 2 }], [{ id: 'a', v: 9 }])
|
|
3131
|
+
* // [{ id: 'a', v: 9 }, { id: 'b', v: 2 }] — incoming order first, base-only survivor after
|
|
3132
|
+
* ```
|
|
3133
|
+
*/
|
|
3134
|
+
export declare function mergeById<T extends {
|
|
3135
|
+
readonly id: string;
|
|
3136
|
+
}>(base: readonly T[], incoming: readonly T[], resolve?: (base: T, incoming: T) => T): readonly T[];
|
|
3137
|
+
|
|
3138
|
+
/**
|
|
3139
|
+
* Reconcile two {@link InferentialDefinition}s onto `base`'s id.
|
|
3140
|
+
*
|
|
3141
|
+
* @remarks
|
|
3142
|
+
* `inferences` and `facts` each merge via {@link mergeById}
|
|
3143
|
+
* (incoming-wins-wholesale on a matched id). Every other scalar field is
|
|
3144
|
+
* incoming-wins-when-present, else base kept.
|
|
3145
|
+
*
|
|
3146
|
+
* @param base - The definition merge targets (its `id` is preserved)
|
|
3147
|
+
* @param incoming - The definition merged in (its order and matches take priority)
|
|
3148
|
+
* @returns A fresh, reconciled definition
|
|
3149
|
+
*
|
|
3150
|
+
* @example
|
|
3151
|
+
* ```ts
|
|
3152
|
+
* import { fact, inferentialDefinition, mergeInferentialDefinition } from '@src/core'
|
|
3153
|
+
*
|
|
3154
|
+
* const base = inferentialDefinition('m', 'M', [fact('f1', 'human', ['a'])], [])
|
|
3155
|
+
* const incoming = inferentialDefinition('m', 'M2', [fact('f2', 'human', ['b'])], [])
|
|
3156
|
+
* mergeInferentialDefinition(base, incoming).facts.map((f) => f.id) // ['f2', 'f1']
|
|
3157
|
+
* ```
|
|
3158
|
+
*/
|
|
3159
|
+
export declare function mergeInferentialDefinition(base: InferentialDefinition, incoming: InferentialDefinition): InferentialDefinition;
|
|
3160
|
+
|
|
3161
|
+
/**
|
|
3162
|
+
* Reconcile two {@link LogicalDefinition}s onto `base`'s id.
|
|
3163
|
+
*
|
|
3164
|
+
* @remarks
|
|
3165
|
+
* `rules` merges via {@link mergeById} (incoming-wins-wholesale on a matched
|
|
3166
|
+
* id). Every other scalar field is incoming-wins-when-present, else base kept.
|
|
3167
|
+
*
|
|
3168
|
+
* @param base - The definition merge targets (its `id` is preserved)
|
|
3169
|
+
* @param incoming - The definition merged in (its order and matches take priority)
|
|
3170
|
+
* @returns A fresh, reconciled definition
|
|
3171
|
+
*
|
|
3172
|
+
* @example
|
|
3173
|
+
* ```ts
|
|
3174
|
+
* import { atom, logicalDefinition, mergeLogicalDefinition, rule } from '@src/core'
|
|
3175
|
+
*
|
|
3176
|
+
* const base = logicalDefinition('e', 'E', [rule('r1', [], atom('a', 'equals', true))])
|
|
3177
|
+
* const incoming = logicalDefinition('e', 'E2', [rule('r2', [], atom('b', 'equals', true))])
|
|
3178
|
+
* mergeLogicalDefinition(base, incoming).rules.map((r) => r.id) // ['r2', 'r1']
|
|
3179
|
+
* ```
|
|
3180
|
+
*/
|
|
3181
|
+
export declare function mergeLogicalDefinition(base: LogicalDefinition, incoming: LogicalDefinition): LogicalDefinition;
|
|
3182
|
+
|
|
3183
|
+
/**
|
|
3184
|
+
* Reconcile two {@link QuantitativeDefinition}s onto `base`'s id.
|
|
3185
|
+
*
|
|
3186
|
+
* @remarks
|
|
3187
|
+
* `groups` merges via {@link mergeById}; a matched (same-id) PAIR of groups
|
|
3188
|
+
* recurses one level deeper — their `factors` also merge via `mergeById` — the
|
|
3189
|
+
* one exception to incoming-wins-wholesale (PROPOSAL.md §9). Every other
|
|
3190
|
+
* scalar / value-object field is incoming-wins-when-present, else base kept.
|
|
3191
|
+
*
|
|
3192
|
+
* @param base - The definition merge targets (its `id` is preserved)
|
|
3193
|
+
* @param incoming - The definition merged in (its order and matches take priority)
|
|
3194
|
+
* @returns A fresh, reconciled definition
|
|
3195
|
+
*
|
|
3196
|
+
* @example
|
|
3197
|
+
* ```ts
|
|
3198
|
+
* import { factorGroup, mergeQuantitativeDefinition, quantitativeDefinition } from '@src/core'
|
|
3199
|
+
*
|
|
3200
|
+
* const base = quantitativeDefinition('risk', 'Risk', [factorGroup('g1', 'sum', [])])
|
|
3201
|
+
* const incoming = quantitativeDefinition('risk', 'Risk v2', [factorGroup('g2', 'sum', [])])
|
|
3202
|
+
* mergeQuantitativeDefinition(base, incoming).groups.map((g) => g.id) // ['g2', 'g1']
|
|
3203
|
+
* ```
|
|
3204
|
+
*/
|
|
3205
|
+
export declare function mergeQuantitativeDefinition(base: QuantitativeDefinition, incoming: QuantitativeDefinition): QuantitativeDefinition;
|
|
3206
|
+
|
|
3207
|
+
/**
|
|
3208
|
+
* Reconcile two {@link Subject}s — incoming-wins spread, with the base `id`
|
|
3209
|
+
* preserved when present.
|
|
3210
|
+
*
|
|
3211
|
+
* @remarks
|
|
3212
|
+
* Mirrors the definition merge's base-id-wins rule: `{ ...base, ...incoming }`
|
|
3213
|
+
* with `base.id` restored afterward when `base` carries an own `id`.
|
|
3214
|
+
*
|
|
3215
|
+
* @param base - The subject merge targets (its `id` is preserved when present)
|
|
3216
|
+
* @param incoming - The subject merged in (its fields take priority)
|
|
3217
|
+
* @returns A fresh, reconciled subject
|
|
3218
|
+
*
|
|
3219
|
+
* @example
|
|
3220
|
+
* ```ts
|
|
3221
|
+
* import { mergeSubjects } from '@src/core'
|
|
3222
|
+
*
|
|
3223
|
+
* mergeSubjects({ id: 's1', age: 30 }, { age: 31, name: 'Alice' })
|
|
3224
|
+
* // { id: 's1', age: 31, name: 'Alice' }
|
|
3225
|
+
* ```
|
|
3226
|
+
*/
|
|
3227
|
+
export declare function mergeSubjects(base: Subject, incoming: Subject): Subject;
|
|
3228
|
+
|
|
3229
|
+
/**
|
|
3230
|
+
* Reconcile two {@link SymbolicDefinition}s onto `base`'s id.
|
|
3231
|
+
*
|
|
3232
|
+
* @remarks
|
|
3233
|
+
* `equations` merges via {@link mergeById} (incoming-wins-wholesale on a
|
|
3234
|
+
* matched id); `variables` is a plain incoming-wins spread
|
|
3235
|
+
* (`{ ...base.variables, ...incoming.variables }`). Every other scalar field
|
|
3236
|
+
* is incoming-wins-when-present, else base kept.
|
|
3237
|
+
*
|
|
3238
|
+
* @param base - The definition merge targets (its `id` is preserved)
|
|
3239
|
+
* @param incoming - The definition merged in (its order, matches, and variables take priority)
|
|
3240
|
+
* @returns A fresh, reconciled definition
|
|
3241
|
+
*
|
|
3242
|
+
* @example
|
|
3243
|
+
* ```ts
|
|
3244
|
+
* import { constant, equation, mergeSymbolicDefinition, symbolicDefinition, variable } from '@src/core'
|
|
3245
|
+
*
|
|
3246
|
+
* const base = symbolicDefinition('e', 'E', [], { variables: { x: 1 } })
|
|
3247
|
+
* const incoming = symbolicDefinition('e', 'E2', [equation('e1', variable('x'), constant(2), 'x')], {
|
|
3248
|
+
* variables: { y: 2 },
|
|
3249
|
+
* })
|
|
3250
|
+
* mergeSymbolicDefinition(base, incoming).variables // { x: 1, y: 2 }
|
|
3251
|
+
* ```
|
|
3252
|
+
*/
|
|
3253
|
+
export declare function mergeSymbolicDefinition(base: SymbolicDefinition, incoming: SymbolicDefinition): SymbolicDefinition;
|
|
3254
|
+
|
|
3255
|
+
/**
|
|
3256
|
+
* A symbolic operation node.
|
|
3257
|
+
*
|
|
3258
|
+
* @remarks
|
|
3259
|
+
* `right` is absent for unary operators (`round` / `ceil` / `floor` / `abs`)
|
|
3260
|
+
* and treated as the constant `0` when absent on a binary operator.
|
|
3261
|
+
*/
|
|
3262
|
+
export declare interface Operation {
|
|
3263
|
+
readonly form: 'operation';
|
|
3264
|
+
readonly operator: MathOperation;
|
|
3265
|
+
readonly left: SymbolicExpression;
|
|
3266
|
+
readonly right?: SymbolicExpression;
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3269
|
+
/**
|
|
3270
|
+
* Build an operation {@link SymbolicExpression} node.
|
|
3271
|
+
*
|
|
3272
|
+
* @remarks
|
|
3273
|
+
* The `right` key is OMITTED when absent — correct for the unary operations
|
|
3274
|
+
* (`round` / `ceil` / `floor` / `abs`); a binary operation with no `right`
|
|
3275
|
+
* treats it as the constant `0`.
|
|
3276
|
+
*
|
|
3277
|
+
* @param operator - The math operation
|
|
3278
|
+
* @param left - The left operand
|
|
3279
|
+
* @param right - The right operand (omit for unary operations)
|
|
3280
|
+
* @returns A fresh operation node
|
|
3281
|
+
*
|
|
3282
|
+
* @example
|
|
3283
|
+
* ```ts
|
|
3284
|
+
* import { constant, operation, variable } from '@src/core'
|
|
3285
|
+
*
|
|
3286
|
+
* operation('add', variable('x'), constant(1))
|
|
3287
|
+
* operation('abs', variable('x')) // unary — no right operand
|
|
3288
|
+
* ```
|
|
3289
|
+
*/
|
|
3290
|
+
export declare const operation: (operator: MathOperation, left: SymbolicExpression, right?: SymbolicExpression) => SymbolicExpression;
|
|
3291
|
+
|
|
3292
|
+
/**
|
|
3293
|
+
* Parse a JSON string into a {@link Definition}, failing safe to `undefined`.
|
|
3294
|
+
*
|
|
3295
|
+
* @remarks
|
|
3296
|
+
* The safe inverse of the builders: `parseJSONAs` composed with the data guard
|
|
3297
|
+
* {@link isDefinition}. A built definition body IS the durable JSON payload —
|
|
3298
|
+
* `JSON.stringify(definition)` round-trips through `parseDefinition`. Two
|
|
3299
|
+
* authoring hazards: a required `Check.value: undefined` drops its key on
|
|
3300
|
+
* `JSON.stringify` (author `null` instead), and a `Fact.terms` element of
|
|
3301
|
+
* `undefined` serializes to `null` (terms must be JSON-safe scalars/strings).
|
|
3302
|
+
*
|
|
3303
|
+
* @param json - The JSON text to parse
|
|
3304
|
+
* @returns A {@link Definition} of any reasoning, or `undefined` when malformed
|
|
3305
|
+
*
|
|
3306
|
+
* @example
|
|
3307
|
+
* ```ts
|
|
3308
|
+
* import { logicalDefinition, parseDefinition } from '@src/core'
|
|
3309
|
+
*
|
|
3310
|
+
* const text = JSON.stringify(logicalDefinition('e', 'E', []))
|
|
3311
|
+
* parseDefinition(text) // the definition, restored
|
|
3312
|
+
* parseDefinition('{}') // undefined — fails safe
|
|
3313
|
+
* ```
|
|
3314
|
+
*/
|
|
3315
|
+
export declare function parseDefinition(json: string): Definition | undefined;
|
|
3316
|
+
|
|
3317
|
+
/**
|
|
3318
|
+
* Insert `item` into an id-keyed collection, deduping any existing element
|
|
3319
|
+
* sharing its id, then placing it at the START (or immediately BEFORE `target`).
|
|
3320
|
+
*
|
|
3321
|
+
* @remarks
|
|
3322
|
+
* Mirrors {@link appendById}'s dedup-then-insert semantics exactly, only the
|
|
3323
|
+
* placement differs: no `target` lands `item` at the start; a `target` lands
|
|
3324
|
+
* it immediately before the element whose `id === target` (searched in the
|
|
3325
|
+
* deduped array). A `target` naming no element throws {@link ReasonError}
|
|
3326
|
+
* (`'TARGET'`).
|
|
3327
|
+
*
|
|
3328
|
+
* @typeParam T - An id-carrying element type
|
|
3329
|
+
* @param items - The collection to insert into
|
|
3330
|
+
* @param item - The element to insert
|
|
3331
|
+
* @param target - Optional id to insert immediately before; prepends at the start when absent
|
|
3332
|
+
* @returns A fresh array with `item` inserted
|
|
3333
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no element in `items`
|
|
3334
|
+
*
|
|
3335
|
+
* @example
|
|
3336
|
+
* ```ts
|
|
3337
|
+
* import { prependById } from '@src/core'
|
|
3338
|
+
*
|
|
3339
|
+
* prependById([{ id: 'a' }, { id: 'b' }], { id: 'c' }) // [c, a, b]
|
|
3340
|
+
* prependById([{ id: 'a' }, { id: 'b' }], { id: 'c' }, 'b') // [a, c, b]
|
|
3341
|
+
* ```
|
|
3342
|
+
*/
|
|
3343
|
+
export declare function prependById<T extends {
|
|
3344
|
+
readonly id: string;
|
|
3345
|
+
}>(items: readonly T[], item: T, target?: string): readonly T[];
|
|
3346
|
+
|
|
3347
|
+
/**
|
|
3348
|
+
* Insert `equation` into a {@link SymbolicDefinition}'s `equations` — dedup-
|
|
3349
|
+
* then-insert at the start, or immediately before `target`.
|
|
3350
|
+
*
|
|
3351
|
+
* @param definition - The definition to insert into
|
|
3352
|
+
* @param source - The equation to insert
|
|
3353
|
+
* @param target - Optional equation id to insert immediately before
|
|
3354
|
+
* @returns A fresh definition with `equation` inserted
|
|
3355
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing equation
|
|
3356
|
+
*
|
|
3357
|
+
* @example
|
|
3358
|
+
* ```ts
|
|
3359
|
+
* import { constant, equation, prependEquation, symbolicDefinition, variable } from '@src/core'
|
|
3360
|
+
*
|
|
3361
|
+
* prependEquation(symbolicDefinition('e', 'E', []), equation('e1', variable('x'), constant(1), 'x'))
|
|
3362
|
+
* ```
|
|
3363
|
+
*/
|
|
3364
|
+
export declare function prependEquation(definition: SymbolicDefinition, source: Equation, target?: string): SymbolicDefinition;
|
|
3365
|
+
|
|
3366
|
+
/**
|
|
3367
|
+
* Insert `fact` into an {@link InferentialDefinition}'s `facts` — dedup-then-
|
|
3368
|
+
* insert at the start, or immediately before `target`.
|
|
3369
|
+
*
|
|
3370
|
+
* @param definition - The definition to insert into
|
|
3371
|
+
* @param source - The fact to insert
|
|
3372
|
+
* @param target - Optional fact id to insert immediately before
|
|
3373
|
+
* @returns A fresh definition with `fact` inserted
|
|
3374
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing fact
|
|
3375
|
+
*
|
|
3376
|
+
* @example
|
|
3377
|
+
* ```ts
|
|
3378
|
+
* import { fact, inferentialDefinition, prependFact } from '@src/core'
|
|
3379
|
+
*
|
|
3380
|
+
* prependFact(inferentialDefinition('m', 'M', [], []), fact('f1', 'human', ['socrates']))
|
|
3381
|
+
* ```
|
|
3382
|
+
*/
|
|
3383
|
+
export declare function prependFact(definition: InferentialDefinition, source: Fact, target?: string): InferentialDefinition;
|
|
3384
|
+
|
|
3385
|
+
/**
|
|
3386
|
+
* Insert `factor` into a {@link FactorGroup}'s `factors` — dedup-then-insert
|
|
3387
|
+
* at the start, or immediately before `target`.
|
|
3388
|
+
*
|
|
3389
|
+
* @param group - The group to insert into
|
|
3390
|
+
* @param factor - The factor to insert
|
|
3391
|
+
* @param target - Optional factor id to insert immediately before
|
|
3392
|
+
* @returns A fresh group with `factor` inserted
|
|
3393
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing factor
|
|
3394
|
+
*
|
|
3395
|
+
* @example
|
|
3396
|
+
* ```ts
|
|
3397
|
+
* import { factorGroup, prependFactor, staticFactor } from '@src/core'
|
|
3398
|
+
*
|
|
3399
|
+
* prependFactor(factorGroup('g1', 'sum', []), staticFactor('f1', 10))
|
|
3400
|
+
* ```
|
|
3401
|
+
*/
|
|
3402
|
+
export declare function prependFactor(group: FactorGroup, factor: Factor, target?: string): FactorGroup;
|
|
3403
|
+
|
|
3404
|
+
/**
|
|
3405
|
+
* Insert `group` into a {@link QuantitativeDefinition}'s `groups` — dedup-then-
|
|
3406
|
+
* insert at the start, or immediately before `target`.
|
|
3407
|
+
*
|
|
3408
|
+
* @param definition - The definition to insert into
|
|
3409
|
+
* @param group - The group to insert
|
|
3410
|
+
* @param target - Optional group id to insert immediately before
|
|
3411
|
+
* @returns A fresh definition with `group` inserted
|
|
3412
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing group
|
|
3413
|
+
*
|
|
3414
|
+
* @example
|
|
3415
|
+
* ```ts
|
|
3416
|
+
* import { factorGroup, prependGroup, quantitativeDefinition } from '@src/core'
|
|
3417
|
+
*
|
|
3418
|
+
* prependGroup(quantitativeDefinition('risk', 'Risk', []), factorGroup('g1', 'sum', []))
|
|
3419
|
+
* ```
|
|
3420
|
+
*/
|
|
3421
|
+
export declare function prependGroup(definition: QuantitativeDefinition, group: FactorGroup, target?: string): QuantitativeDefinition;
|
|
3422
|
+
|
|
3423
|
+
/**
|
|
3424
|
+
* Insert `inference` into an {@link InferentialDefinition}'s `inferences` —
|
|
3425
|
+
* dedup-then-insert at the start, or immediately before `target`.
|
|
3426
|
+
*
|
|
3427
|
+
* @param definition - The definition to insert into
|
|
3428
|
+
* @param source - The inference to insert
|
|
3429
|
+
* @param target - Optional inference id to insert immediately before
|
|
3430
|
+
* @returns A fresh definition with `inference` inserted
|
|
3431
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing inference
|
|
3432
|
+
*
|
|
3433
|
+
* @example
|
|
3434
|
+
* ```ts
|
|
3435
|
+
* import { fact, inference, inferentialDefinition, prependInference } from '@src/core'
|
|
3436
|
+
*
|
|
3437
|
+
* prependInference(
|
|
3438
|
+
* inferentialDefinition('m', 'M', [], []),
|
|
3439
|
+
* inference('i1', [fact('p', 'human', ['?x'])], fact('c', 'mortal', ['?x'])),
|
|
3440
|
+
* )
|
|
3441
|
+
* ```
|
|
3442
|
+
*/
|
|
3443
|
+
export declare function prependInference(definition: InferentialDefinition, source: Inference, target?: string): InferentialDefinition;
|
|
3444
|
+
|
|
3445
|
+
/**
|
|
3446
|
+
* Insert `rule` into a {@link LogicalDefinition}'s `rules` — dedup-then-insert
|
|
3447
|
+
* at the start, or immediately before `target`.
|
|
3448
|
+
*
|
|
3449
|
+
* @param definition - The definition to insert into
|
|
3450
|
+
* @param source - The rule to insert
|
|
3451
|
+
* @param target - Optional rule id to insert immediately before
|
|
3452
|
+
* @returns A fresh definition with `rule` inserted
|
|
3453
|
+
* @throws {@link ReasonError} `'TARGET'` when `target` names no existing rule
|
|
3454
|
+
*
|
|
3455
|
+
* @example
|
|
3456
|
+
* ```ts
|
|
3457
|
+
* import { atom, logicalDefinition, prependRule, rule } from '@src/core'
|
|
3458
|
+
*
|
|
3459
|
+
* prependRule(logicalDefinition('e', 'E', []), rule('r1', [], atom('a', 'equals', true)))
|
|
3460
|
+
* ```
|
|
3461
|
+
*/
|
|
3462
|
+
export declare function prependRule(definition: LogicalDefinition, source: Rule, target?: string): LogicalDefinition;
|
|
3463
|
+
|
|
3464
|
+
/**
|
|
3465
|
+
* One node of a backward-chaining proof tree.
|
|
3466
|
+
*
|
|
3467
|
+
* @remarks
|
|
3468
|
+
* `fact` is the proved fact's / goal's id; `inference` is set when the node was
|
|
3469
|
+
* derived through an inference (absent on a base-fact leaf); `children` are the
|
|
3470
|
+
* sub-proofs of that inference's premises; `depth` is the recursion depth the
|
|
3471
|
+
* node was proved at.
|
|
3472
|
+
*/
|
|
3473
|
+
export declare interface ProofNode {
|
|
3474
|
+
readonly fact: string;
|
|
3475
|
+
readonly inference?: string;
|
|
3476
|
+
readonly children?: readonly ProofNode[];
|
|
3477
|
+
readonly depth: number;
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
/** Default `id` for a `QuantitativeReasoner`. */
|
|
3481
|
+
export declare const QUANTITATIVE_ID = "quantitative";
|
|
3482
|
+
|
|
3483
|
+
/**
|
|
3484
|
+
* A quantitative (factor-based numeric scoring) definition.
|
|
3485
|
+
*
|
|
3486
|
+
* @remarks
|
|
3487
|
+
* The final value is `base` (default `0`) plus the aggregation of the applied
|
|
3488
|
+
* groups' values (NO weights at this level), clamped to `bounds`, then rounded
|
|
3489
|
+
* to `precision` decimal places (default `4`).
|
|
3490
|
+
*/
|
|
3491
|
+
export declare interface QuantitativeDefinition {
|
|
3492
|
+
readonly reasoning: 'quantitative';
|
|
3493
|
+
readonly id: string;
|
|
3494
|
+
readonly name: string;
|
|
3495
|
+
readonly description?: string;
|
|
3496
|
+
readonly groups: readonly FactorGroup[];
|
|
3497
|
+
readonly aggregation: Aggregation;
|
|
3498
|
+
readonly base?: number;
|
|
3499
|
+
readonly bounds?: Bounds;
|
|
3500
|
+
readonly precision?: number;
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3503
|
+
/**
|
|
3504
|
+
* Build a {@link QuantitativeDefinition}.
|
|
3505
|
+
*
|
|
3506
|
+
* @remarks
|
|
3507
|
+
* `aggregation` defaults to `'sum'`; set `aggregation`, `description`, `base`,
|
|
3508
|
+
* `bounds`, or `precision` through `overrides`.
|
|
3509
|
+
*
|
|
3510
|
+
* @param id - The definition id
|
|
3511
|
+
* @param name - The display name
|
|
3512
|
+
* @param groups - The factor groups
|
|
3513
|
+
* @param overrides - Optional {@link QuantitativeDefinition} fields merged over the defaults
|
|
3514
|
+
* @returns A fresh quantitative definition
|
|
3515
|
+
*
|
|
3516
|
+
* @example
|
|
3517
|
+
* ```ts
|
|
3518
|
+
* import { factorGroup, fieldFactor, quantitativeDefinition } from '@src/core'
|
|
3519
|
+
*
|
|
3520
|
+
* quantitativeDefinition('risk', 'Risk Score', [factorGroup('g1', 'sum', [fieldFactor('age', 'age')])], {
|
|
3521
|
+
* base: 100,
|
|
3522
|
+
* })
|
|
3523
|
+
* ```
|
|
3524
|
+
*/
|
|
3525
|
+
export declare function quantitativeDefinition(id: string, name: string, groups: readonly FactorGroup[], overrides?: Partial<Omit<QuantitativeDefinition, 'reasoning' | 'id' | 'name' | 'groups'>>): QuantitativeDefinition;
|
|
3526
|
+
|
|
3527
|
+
/**
|
|
3528
|
+
* The quantitative reasoner — factor-based numeric scoring.
|
|
3529
|
+
*
|
|
3530
|
+
* @remarks
|
|
3531
|
+
* Each factor runs a fixed pipeline: checks gate (ALL met) → source resolve
|
|
3532
|
+
* (`fallback` when unresolvable) → finite check → transforms chain → bounds
|
|
3533
|
+
* clamp → finite recheck. Factors evaluate in stable ascending `priority`
|
|
3534
|
+
* order; a `strict` group is all-or-nothing; a group's value is its `base` plus
|
|
3535
|
+
* the weighted aggregation of its APPLIED factors, clamped (never rounded); the
|
|
3536
|
+
* definition's value is its `base` plus the unweighted aggregation of the
|
|
3537
|
+
* APPLIED groups' values, clamped, then rounded to `precision`. A
|
|
3538
|
+
* required-factor failure or non-finite value appends an error (`success:
|
|
3539
|
+
* false`) without aborting — the numeric `value` is always computed. The
|
|
3540
|
+
* definition-level value is finite-checked AFTER rounding: a non-finite
|
|
3541
|
+
* aggregate (a `minimum` / `maximum` over zero applied groups) appends an error
|
|
3542
|
+
* while the `NaN` stays visible in `value`. Field sources coerce through the
|
|
3543
|
+
* contracts `parseNumberField`: a non-finite subject number or a non-numeric
|
|
3544
|
+
* string is unresolvable and takes the fallback path. Lookup sources read only
|
|
3545
|
+
* OWN table keys, and a missing / `null` field falls back directly. Nothing
|
|
3546
|
+
* mutates its inputs; fully deterministic (AGENTS §11).
|
|
3547
|
+
*/
|
|
3548
|
+
export declare class QuantitativeReasoner implements ReasonerInterface {
|
|
3549
|
+
#private;
|
|
3550
|
+
constructor(options?: QuantitativeReasonerOptions);
|
|
3551
|
+
get id(): string;
|
|
3552
|
+
get reasoning(): Reasoning;
|
|
3553
|
+
supports(definition: Definition): boolean;
|
|
3554
|
+
validate(definition: Definition): ReasonValidationResult;
|
|
3555
|
+
reason(subject: Subject, definition: Definition): ReasonResult;
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3558
|
+
/**
|
|
3559
|
+
* Options for `createQuantitativeReasoner` / the `QuantitativeReasoner`
|
|
3560
|
+
* constructor.
|
|
3561
|
+
*
|
|
3562
|
+
* @remarks
|
|
3563
|
+
* `id` — the reasoner's identity string (defaults to `QUANTITATIVE_ID`).
|
|
3564
|
+
* `evaluator` / `transformer` / `aggregator` — injectable operators (each
|
|
3565
|
+
* defaults to a fresh default-constructed instance).
|
|
3566
|
+
*/
|
|
3567
|
+
export declare interface QuantitativeReasonerOptions {
|
|
3568
|
+
readonly id?: string;
|
|
3569
|
+
readonly evaluator?: EvaluatorInterface;
|
|
3570
|
+
readonly transformer?: TransformerInterface;
|
|
3571
|
+
readonly aggregator?: AggregatorInterface;
|
|
3572
|
+
}
|
|
3573
|
+
|
|
3574
|
+
/**
|
|
3575
|
+
* The outcome of quantitative reasoning.
|
|
3576
|
+
*
|
|
3577
|
+
* @remarks
|
|
3578
|
+
* `count` tallies the applied groups. `success` is `false` whenever any error
|
|
3579
|
+
* accumulated (a required-factor failure, a non-finite value) — the numeric
|
|
3580
|
+
* `value` is still computed.
|
|
3581
|
+
*/
|
|
3582
|
+
export declare interface QuantitativeResult {
|
|
3583
|
+
readonly reasoning: 'quantitative';
|
|
3584
|
+
readonly value: number;
|
|
3585
|
+
readonly groups: readonly GroupResult[];
|
|
3586
|
+
readonly count: number;
|
|
3587
|
+
readonly success: boolean;
|
|
3588
|
+
readonly trace: readonly string[];
|
|
3589
|
+
readonly errors: readonly string[];
|
|
3590
|
+
}
|
|
3591
|
+
|
|
3592
|
+
/**
|
|
3593
|
+
* Build a {@link Factor} over a range {@link Source}.
|
|
3594
|
+
*
|
|
3595
|
+
* @param id - The factor id
|
|
3596
|
+
* @param field - The subject field to resolve as a number
|
|
3597
|
+
* @param ranges - The bands, scanned in order (first match wins)
|
|
3598
|
+
* @param overrides - Optional {@link Factor} fields merged over the defaults
|
|
3599
|
+
* @returns A fresh factor
|
|
3600
|
+
*
|
|
3601
|
+
* @example
|
|
3602
|
+
* ```ts
|
|
3603
|
+
* import { bounds, rangeFactor } from '@src/core'
|
|
3604
|
+
*
|
|
3605
|
+
* rangeFactor('age-band', 'age', [{ bounds: bounds(undefined, 24), value: 30 }])
|
|
3606
|
+
* ```
|
|
3607
|
+
*/
|
|
3608
|
+
export declare function rangeFactor(id: string, field: FieldPath, ranges: readonly FactorRange[], overrides?: Partial<Omit<Factor, 'id' | 'source'>>): Factor;
|
|
3609
|
+
|
|
3610
|
+
/**
|
|
3611
|
+
* A factor source banding a numeric subject field through ordered ranges.
|
|
3612
|
+
*
|
|
3613
|
+
* @remarks
|
|
3614
|
+
* Ranges are scanned in order and the FIRST match wins. A range without
|
|
3615
|
+
* `bounds` matches anything (a catch-all); an absent bound side is open. No
|
|
3616
|
+
* match falls back to the factor's `fallback`.
|
|
3617
|
+
*/
|
|
3618
|
+
export declare interface RangeSource {
|
|
3619
|
+
readonly origin: 'range';
|
|
3620
|
+
readonly field: FieldPath;
|
|
3621
|
+
readonly ranges: readonly FactorRange[];
|
|
3622
|
+
}
|
|
3623
|
+
|
|
3624
|
+
/**
|
|
3625
|
+
* Build a range {@link Source} — a numeric subject field banded through ordered
|
|
3626
|
+
* ranges (first match wins).
|
|
3627
|
+
*
|
|
3628
|
+
* @param field - The subject field to resolve as a number
|
|
3629
|
+
* @param ranges - The bands, scanned in order
|
|
3630
|
+
* @returns A fresh range source
|
|
3631
|
+
*
|
|
3632
|
+
* @example
|
|
3633
|
+
* ```ts
|
|
3634
|
+
* import { bounds, rangeSource } from '@src/core'
|
|
3635
|
+
*
|
|
3636
|
+
* rangeSource('age', [
|
|
3637
|
+
* { bounds: bounds(undefined, 24), value: 30 },
|
|
3638
|
+
* { bounds: bounds(25, 64), value: 15 },
|
|
3639
|
+
* { bounds: bounds(65), value: 10 },
|
|
3640
|
+
* ])
|
|
3641
|
+
* ```
|
|
3642
|
+
*/
|
|
3643
|
+
export declare function rangeSource(field: FieldPath, ranges: readonly FactorRange[]): Source;
|
|
3644
|
+
|
|
3645
|
+
/**
|
|
3646
|
+
* The reasoning orchestrator — a thin router over registered
|
|
3647
|
+
* {@link ReasonerInterface}s.
|
|
3648
|
+
*
|
|
3649
|
+
* @remarks
|
|
3650
|
+
* Holds NO strategy-specific logic: dispatch is purely a registry lookup by
|
|
3651
|
+
* `definition.reasoning` (one reasoner per reasoning; re-registration
|
|
3652
|
+
* replaces). A missing reasoner throws `MISSING` and a pre-run validation
|
|
3653
|
+
* failure (when the `validate` option is on) throws `INVALID` — both BYPASS
|
|
3654
|
+
* `bail` and emit nothing. A reasoner throw emits `error` with the raw thrown
|
|
3655
|
+
* value, then rethrows under `bail: true` (the default) or converts to a
|
|
3656
|
+
* type-shaped failure result under `bail: false`; only SUCCESSFUL results emit
|
|
3657
|
+
* `reason` (synchronously, before returning). The batch overload maps subjects
|
|
3658
|
+
* in order (validation, when on, repeats per subject). `destroy()` clears the
|
|
3659
|
+
* registry, emits `destroy`, then destroys the emitter LAST (AGENTS §13) and is
|
|
3660
|
+
* idempotent; every other method afterwards throws `DESTROYED` — only the
|
|
3661
|
+
* {@link emitter} getter keeps working.
|
|
3662
|
+
*/
|
|
3663
|
+
export declare class Reason implements ReasonInterface {
|
|
3664
|
+
#private;
|
|
3665
|
+
constructor(options?: ReasonOptions);
|
|
3666
|
+
get emitter(): EmitterInterface<ReasonEventMap>;
|
|
3667
|
+
reason(subjects: readonly Subject[], definition: Definition): readonly ReasonResult[];
|
|
3668
|
+
reason(subject: Subject, definition: Definition): ReasonResult;
|
|
3669
|
+
register(reasoner: ReasonerInterface): void;
|
|
3670
|
+
reasoner(reasoning: Reasoning): ReasonerInterface | undefined;
|
|
3671
|
+
reasoners(): readonly ReasonerInterface[];
|
|
3672
|
+
supports(reasoning: Reasoning): boolean;
|
|
3673
|
+
validate(definition: Definition): ReasonValidationResult;
|
|
3674
|
+
destroy(): void;
|
|
3675
|
+
}
|
|
3676
|
+
|
|
3677
|
+
/**
|
|
3678
|
+
* A reasoning strategy adapter — one per {@link Reasoning}.
|
|
3679
|
+
*
|
|
3680
|
+
* @remarks
|
|
3681
|
+
* `reason` throws a `ReasonError` (`MISMATCH`) when handed a definition of a
|
|
3682
|
+
* different reasoning; every other malformation yields a failure RESULT, not a
|
|
3683
|
+
* throw — the runtime never assumes `validate` ran. `supports` / `validate` /
|
|
3684
|
+
* `reason` each take plain data only — a {@link DefinitionBuilderInterface} /
|
|
3685
|
+
* {@link SubjectBuilderInterface}'s `build()` output is passed instead, by the
|
|
3686
|
+
* caller.
|
|
3687
|
+
*/
|
|
3688
|
+
export declare interface ReasonerInterface {
|
|
3689
|
+
readonly id: string;
|
|
3690
|
+
readonly reasoning: Reasoning;
|
|
3691
|
+
supports(definition: Definition): boolean;
|
|
3692
|
+
validate(definition: Definition): ReasonValidationResult;
|
|
3693
|
+
reason(subject: Subject, definition: Definition): ReasonResult;
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
/**
|
|
3697
|
+
* An error thrown by the reasons layer.
|
|
3698
|
+
*
|
|
3699
|
+
* @remarks
|
|
3700
|
+
* Thrown for: dispatching a definition no registered reasoner handles
|
|
3701
|
+
* (`MISSING`), a pre-run validation failure when the orchestrator's `validate`
|
|
3702
|
+
* option is on (`INVALID`), handing a reasoner a definition of a different
|
|
3703
|
+
* reasoning (`MISMATCH`), any use of a destroyed orchestrator (`DESTROYED`),
|
|
3704
|
+
* and an `appendById` / `prependById` (or per-kind `append*` / `prepend*`)
|
|
3705
|
+
* `target` id naming no existing element (`TARGET`). `context`, when present,
|
|
3706
|
+
* carries the definition id and the reasoning involved (or, for `TARGET`, the
|
|
3707
|
+
* offending `id` / `target`).
|
|
3708
|
+
*/
|
|
3709
|
+
export declare class ReasonError extends Error {
|
|
3710
|
+
readonly code: ReasonErrorCode;
|
|
3711
|
+
readonly context?: Readonly<Record<string, unknown>>;
|
|
3712
|
+
constructor(code: ReasonErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
|
|
3713
|
+
}
|
|
3714
|
+
|
|
3715
|
+
/**
|
|
3716
|
+
* A machine-readable `ReasonError` code.
|
|
3717
|
+
*
|
|
3718
|
+
* @remarks
|
|
3719
|
+
* `MISSING` — no reasoner registered for the definition's reasoning.
|
|
3720
|
+
* `INVALID` — pre-run validation failed (`validate: true`). `MISMATCH` — a
|
|
3721
|
+
* reasoner was handed a definition of a different reasoning. `DESTROYED` — the
|
|
3722
|
+
* orchestrator was used after `destroy()`. `TARGET` — a locator id names no
|
|
3723
|
+
* element that exists in the collection it addresses: an optional `target` id
|
|
3724
|
+
* passed to `appendById` / `prependById` (and the per-kind `append*` /
|
|
3725
|
+
* `prepend*` helpers built on them) that names no existing element.
|
|
3726
|
+
*/
|
|
3727
|
+
export declare type ReasonErrorCode = 'MISSING' | 'INVALID' | 'MISMATCH' | 'DESTROYED' | 'TARGET';
|
|
3728
|
+
|
|
3729
|
+
/**
|
|
3730
|
+
* The push observation surface of a {@link ReasonInterface} (AGENTS §13).
|
|
3731
|
+
*
|
|
3732
|
+
* @remarks
|
|
3733
|
+
* `register` fires when a reasoner is registered (carrying its reasoning);
|
|
3734
|
+
* `reason` fires once per SUCCESSFUL result, synchronously before it returns
|
|
3735
|
+
* (bail-suppressed failure results do not fire it); `error` fires with the raw
|
|
3736
|
+
* thrown value when a reasoner throws (regardless of `bail`); `destroy` fires
|
|
3737
|
+
* once on teardown. Listener isolation is the emitter's own — a throwing
|
|
3738
|
+
* listener routes to the `error` OPTION handler, never onto this map.
|
|
3739
|
+
*/
|
|
3740
|
+
export declare type ReasonEventMap = {
|
|
3741
|
+
/** A reasoner was registered — carries its reasoning. */
|
|
3742
|
+
readonly register: readonly [reasoning: Reasoning];
|
|
3743
|
+
/** A reasoning run succeeded — carries the produced result. */
|
|
3744
|
+
readonly reason: readonly [result: ReasonResult];
|
|
3745
|
+
/** A reasoner threw — carries the raw thrown value. */
|
|
3746
|
+
readonly error: readonly [error: unknown];
|
|
3747
|
+
/** The orchestrator was destroyed. */
|
|
3748
|
+
readonly destroy: readonly [];
|
|
3749
|
+
};
|
|
3750
|
+
|
|
3751
|
+
/**
|
|
3752
|
+
* The four reasoning strategies — the axis a {@link Definition} /
|
|
3753
|
+
* {@link ReasonResult} discriminates on.
|
|
3754
|
+
*
|
|
3755
|
+
* @remarks
|
|
3756
|
+
* `quantitative` — factor-based numeric scoring. `logical` — rule-based boolean
|
|
3757
|
+
* deduction with forward / backward chaining. `symbolic` — algebraic equation
|
|
3758
|
+
* solving by variable isolation. `inferential` — fact derivation with
|
|
3759
|
+
* unification variables and proof trees.
|
|
3760
|
+
*/
|
|
3761
|
+
export declare type Reasoning = 'quantitative' | 'logical' | 'symbolic' | 'inferential';
|
|
3762
|
+
|
|
3763
|
+
/**
|
|
3764
|
+
* The reasoning orchestrator — a thin router over registered
|
|
3765
|
+
* {@link ReasonerInterface}s.
|
|
3766
|
+
*
|
|
3767
|
+
* @remarks
|
|
3768
|
+
* Dispatch is purely by `definition.reasoning` registry lookup; a missing
|
|
3769
|
+
* reasoner throws `MISSING` (never subject to `bail`, no `error` emit). The
|
|
3770
|
+
* batch `reason` overload maps subjects in order to an equal-length result
|
|
3771
|
+
* array. After `destroy()` every method except the `emitter` getter and
|
|
3772
|
+
* `destroy` itself throws `DESTROYED`. `reason` and `validate` each take
|
|
3773
|
+
* plain data only — a {@link DefinitionBuilderInterface} /
|
|
3774
|
+
* {@link SubjectBuilderInterface}'s `build()` output is passed instead, by
|
|
3775
|
+
* the caller.
|
|
3776
|
+
*/
|
|
3777
|
+
export declare interface ReasonInterface {
|
|
3778
|
+
readonly emitter: EmitterInterface<ReasonEventMap>;
|
|
3779
|
+
reason(subjects: readonly Subject[], definition: Definition): readonly ReasonResult[];
|
|
3780
|
+
reason(subject: Subject, definition: Definition): ReasonResult;
|
|
3781
|
+
register(reasoner: ReasonerInterface): void;
|
|
3782
|
+
reasoner(reasoning: Reasoning): ReasonerInterface | undefined;
|
|
3783
|
+
reasoners(): readonly ReasonerInterface[];
|
|
3784
|
+
supports(reasoning: Reasoning): boolean;
|
|
3785
|
+
validate(definition: Definition): ReasonValidationResult;
|
|
3786
|
+
destroy(): void;
|
|
3787
|
+
}
|
|
3788
|
+
|
|
3789
|
+
/**
|
|
3790
|
+
* Options for `createReason` / the `Reason` constructor.
|
|
3791
|
+
*
|
|
3792
|
+
* @remarks
|
|
3793
|
+
* `reasoners` — the initial registry (a later entry of the same reasoning
|
|
3794
|
+
* replaces an earlier one). `bail` — when `true` (the default) a reasoner
|
|
3795
|
+
* throw is rethrown after the `error` emit; when `false` it becomes a failure
|
|
3796
|
+
* result. `validate` — when `true`, every `reason` call validates the
|
|
3797
|
+
* definition first and throws `INVALID` on failure (default `false`). `on` —
|
|
3798
|
+
* initial event listeners (AGENTS §8). `error` — the emitter's listener-error
|
|
3799
|
+
* handler (AGENTS §13).
|
|
3800
|
+
*/
|
|
3801
|
+
export declare interface ReasonOptions {
|
|
3802
|
+
readonly reasoners?: readonly ReasonerInterface[];
|
|
3803
|
+
readonly bail?: boolean;
|
|
3804
|
+
readonly validate?: boolean;
|
|
3805
|
+
readonly on?: EmitterHooks<ReasonEventMap>;
|
|
3806
|
+
readonly error?: EmitterErrorHandler;
|
|
3807
|
+
}
|
|
3808
|
+
|
|
3809
|
+
/** Any reasoning result, discriminated by `reasoning`. */
|
|
3810
|
+
export declare type ReasonResult = QuantitativeResult | LogicalResult | SymbolicResult | InferentialResult;
|
|
3811
|
+
|
|
3812
|
+
/**
|
|
3813
|
+
* The outcome of validating a definition — hard `errors` (definition unusable)
|
|
3814
|
+
* and soft `warnings` (suspicious but runnable). `valid` is `true` exactly when
|
|
3815
|
+
* `errors` is empty.
|
|
3816
|
+
*
|
|
3817
|
+
* @remarks
|
|
3818
|
+
* Warnings cover empty collections, duplicate ids (`Duplicate <noun> id`),
|
|
3819
|
+
* inferential confidences outside `[0, 1]`, a logical conclusion's array-path
|
|
3820
|
+
* overlay key also read via an array-path premise elsewhere (the flat overlay
|
|
3821
|
+
* key will not resolve), and an inferential conclusion carrying a `?variable`
|
|
3822
|
+
* unbound by all of its inference's premises — the runtime stays permissive
|
|
3823
|
+
* about all of them.
|
|
3824
|
+
*/
|
|
3825
|
+
export declare interface ReasonValidationResult {
|
|
3826
|
+
readonly valid: boolean;
|
|
3827
|
+
readonly errors: readonly string[];
|
|
3828
|
+
readonly warnings: readonly string[];
|
|
3829
|
+
}
|
|
3830
|
+
|
|
3831
|
+
/**
|
|
3832
|
+
* Filter every element sharing `id` out of an id-keyed collection.
|
|
3833
|
+
*
|
|
3834
|
+
* @remarks
|
|
3835
|
+
* An absent `id` yields a same-length fresh copy — a no-op, never a throw.
|
|
3836
|
+
*
|
|
3837
|
+
* @typeParam T - An id-carrying element type
|
|
3838
|
+
* @param items - The collection to remove from
|
|
3839
|
+
* @param id - The id to remove every occurrence of
|
|
3840
|
+
* @returns A fresh array with every `id`-matching element removed
|
|
3841
|
+
*
|
|
3842
|
+
* @example
|
|
3843
|
+
* ```ts
|
|
3844
|
+
* import { removeById } from '@src/core'
|
|
3845
|
+
*
|
|
3846
|
+
* removeById([{ id: 'a' }, { id: 'b' }], 'a') // [{ id: 'b' }]
|
|
3847
|
+
* removeById([{ id: 'a' }], 'z') // [{ id: 'a' }] — no-op
|
|
3848
|
+
* ```
|
|
3849
|
+
*/
|
|
3850
|
+
export declare function removeById<T extends {
|
|
3851
|
+
readonly id: string;
|
|
3852
|
+
}>(items: readonly T[], id: string): readonly T[];
|
|
3853
|
+
|
|
3854
|
+
/**
|
|
3855
|
+
* Remove every equation sharing `id` from a {@link SymbolicDefinition} (no-op
|
|
3856
|
+
* when absent).
|
|
3857
|
+
*
|
|
3858
|
+
* @param definition - The definition to update
|
|
3859
|
+
* @param id - The equation id to remove
|
|
3860
|
+
* @returns A fresh definition with the equation removed
|
|
3861
|
+
*
|
|
3862
|
+
* @example
|
|
3863
|
+
* ```ts
|
|
3864
|
+
* import { constant, equation, removeEquation, symbolicDefinition, variable } from '@src/core'
|
|
3865
|
+
*
|
|
3866
|
+
* const definition = symbolicDefinition('e', 'E', [equation('e1', variable('x'), constant(1), 'x')])
|
|
3867
|
+
* removeEquation(definition, 'e1').equations // []
|
|
3868
|
+
* ```
|
|
3869
|
+
*/
|
|
3870
|
+
export declare function removeEquation(definition: SymbolicDefinition, id: string): SymbolicDefinition;
|
|
3871
|
+
|
|
3872
|
+
/**
|
|
3873
|
+
* Remove every fact sharing `id` from an {@link InferentialDefinition} (no-op
|
|
3874
|
+
* when absent).
|
|
3875
|
+
*
|
|
3876
|
+
* @param definition - The definition to update
|
|
3877
|
+
* @param id - The fact id to remove
|
|
3878
|
+
* @returns A fresh definition with the fact removed
|
|
3879
|
+
*
|
|
3880
|
+
* @example
|
|
3881
|
+
* ```ts
|
|
3882
|
+
* import { fact, inferentialDefinition, removeFact } from '@src/core'
|
|
3883
|
+
*
|
|
3884
|
+
* const definition = inferentialDefinition('m', 'M', [fact('f1', 'human', ['socrates'])], [])
|
|
3885
|
+
* removeFact(definition, 'f1').facts // []
|
|
3886
|
+
* ```
|
|
3887
|
+
*/
|
|
3888
|
+
export declare function removeFact(definition: InferentialDefinition, id: string): InferentialDefinition;
|
|
3889
|
+
|
|
3890
|
+
/**
|
|
3891
|
+
* Remove every factor sharing `id` from a {@link FactorGroup} (no-op when
|
|
3892
|
+
* absent).
|
|
3893
|
+
*
|
|
3894
|
+
* @param group - The group to update
|
|
3895
|
+
* @param id - The factor id to remove
|
|
3896
|
+
* @returns A fresh group with the factor removed
|
|
3897
|
+
*
|
|
3898
|
+
* @example
|
|
3899
|
+
* ```ts
|
|
3900
|
+
* import { factorGroup, removeFactor, staticFactor } from '@src/core'
|
|
3901
|
+
*
|
|
3902
|
+
* removeFactor(factorGroup('g1', 'sum', [staticFactor('f1', 10)]), 'f1').factors // []
|
|
3903
|
+
* ```
|
|
3904
|
+
*/
|
|
3905
|
+
export declare function removeFactor(group: FactorGroup, id: string): FactorGroup;
|
|
3906
|
+
|
|
3907
|
+
/**
|
|
3908
|
+
* Delete one field of a {@link Subject} — destructure-rest omit.
|
|
3909
|
+
*
|
|
3910
|
+
* @remarks
|
|
3911
|
+
* The key is DELETED entirely (never set to `undefined`), keeping the result
|
|
3912
|
+
* exact-record valid. A no-op (fresh copy) when `key` is absent.
|
|
3913
|
+
*
|
|
3914
|
+
* @param subject - The subject to update
|
|
3915
|
+
* @param key - The field to delete
|
|
3916
|
+
* @returns A fresh subject with `key` omitted
|
|
3917
|
+
*
|
|
3918
|
+
* @example
|
|
3919
|
+
* ```ts
|
|
3920
|
+
* import { removeField } from '@src/core'
|
|
3921
|
+
*
|
|
3922
|
+
* removeField({ id: 's1', age: 30 }, 'age') // { id: 's1' }
|
|
3923
|
+
* ```
|
|
3924
|
+
*/
|
|
3925
|
+
export declare function removeField(subject: Subject, key: string): Subject;
|
|
3926
|
+
|
|
3927
|
+
/**
|
|
3928
|
+
* Remove every group sharing `id` from a {@link QuantitativeDefinition}
|
|
3929
|
+
* (no-op when absent).
|
|
3930
|
+
*
|
|
3931
|
+
* @param definition - The definition to update
|
|
3932
|
+
* @param id - The group id to remove
|
|
3933
|
+
* @returns A fresh definition with the group removed
|
|
3934
|
+
*
|
|
3935
|
+
* @example
|
|
3936
|
+
* ```ts
|
|
3937
|
+
* import { factorGroup, quantitativeDefinition, removeGroup } from '@src/core'
|
|
3938
|
+
*
|
|
3939
|
+
* const definition = quantitativeDefinition('risk', 'Risk', [factorGroup('g1', 'sum', [])])
|
|
3940
|
+
* removeGroup(definition, 'g1').groups // []
|
|
3941
|
+
* ```
|
|
3942
|
+
*/
|
|
3943
|
+
export declare function removeGroup(definition: QuantitativeDefinition, id: string): QuantitativeDefinition;
|
|
3944
|
+
|
|
3945
|
+
/**
|
|
3946
|
+
* Remove every inference sharing `id` from an {@link InferentialDefinition}
|
|
3947
|
+
* (no-op when absent).
|
|
3948
|
+
*
|
|
3949
|
+
* @param definition - The definition to update
|
|
3950
|
+
* @param id - The inference id to remove
|
|
3951
|
+
* @returns A fresh definition with the inference removed
|
|
3952
|
+
*
|
|
3953
|
+
* @example
|
|
3954
|
+
* ```ts
|
|
3955
|
+
* import { fact, inference, inferentialDefinition, removeInference } from '@src/core'
|
|
3956
|
+
*
|
|
3957
|
+
* const original = inference('i1', [fact('p', 'human', ['?x'])], fact('c', 'mortal', ['?x']))
|
|
3958
|
+
* removeInference(inferentialDefinition('m', 'M', [], [original]), 'i1').inferences // []
|
|
3959
|
+
* ```
|
|
3960
|
+
*/
|
|
3961
|
+
export declare function removeInference(definition: InferentialDefinition, id: string): InferentialDefinition;
|
|
3962
|
+
|
|
3963
|
+
/**
|
|
3964
|
+
* Remove every rule sharing `id` from a {@link LogicalDefinition} (no-op when
|
|
3965
|
+
* absent).
|
|
3966
|
+
*
|
|
3967
|
+
* @param definition - The definition to update
|
|
3968
|
+
* @param id - The rule id to remove
|
|
3969
|
+
* @returns A fresh definition with the rule removed
|
|
3970
|
+
*
|
|
3971
|
+
* @example
|
|
3972
|
+
* ```ts
|
|
3973
|
+
* import { atom, logicalDefinition, removeRule, rule } from '@src/core'
|
|
3974
|
+
*
|
|
3975
|
+
* const definition = logicalDefinition('e', 'E', [rule('r1', [], atom('a', 'equals', true))])
|
|
3976
|
+
* removeRule(definition, 'r1').rules // []
|
|
3977
|
+
* ```
|
|
3978
|
+
*/
|
|
3979
|
+
export declare function removeRule(definition: LogicalDefinition, id: string): LogicalDefinition;
|
|
3980
|
+
|
|
3981
|
+
/**
|
|
3982
|
+
* Remove one entry of a {@link SymbolicDefinition}'s `variables`.
|
|
3983
|
+
*
|
|
3984
|
+
* @remarks
|
|
3985
|
+
* The destructure-rest form OMITS the key entirely (never sets it to
|
|
3986
|
+
* `undefined`), keeping the result exact-record valid. A no-op (fresh copy)
|
|
3987
|
+
* when `name` is absent.
|
|
3988
|
+
*
|
|
3989
|
+
* @param definition - The definition to update
|
|
3990
|
+
* @param name - The variable name to remove
|
|
3991
|
+
* @returns A fresh definition with the variable removed
|
|
3992
|
+
*
|
|
3993
|
+
* @example
|
|
3994
|
+
* ```ts
|
|
3995
|
+
* import { removeVariable, symbolicDefinition } from '@src/core'
|
|
3996
|
+
*
|
|
3997
|
+
* removeVariable(symbolicDefinition('e', 'E', [], { variables: { x: 5 } }), 'x').variables // {}
|
|
3998
|
+
* ```
|
|
3999
|
+
*/
|
|
4000
|
+
export declare function removeVariable(definition: SymbolicDefinition, name: string): SymbolicDefinition;
|
|
4001
|
+
|
|
4002
|
+
/**
|
|
4003
|
+
* Produce `count` deterministic clones of a {@link Subject}.
|
|
4004
|
+
*
|
|
4005
|
+
* @remarks
|
|
4006
|
+
* When `subject.id` is a string, each clone's id is minted
|
|
4007
|
+
* `` `${baseId}-${index}` `` (index from `0`); with no string `id`, the clones
|
|
4008
|
+
* pass through unchanged (still fresh copies). Pure and deterministic — the
|
|
4009
|
+
* same input always produces the same output (run-twice equality) — and does
|
|
4010
|
+
* NOT emit. `count <= 0` yields an empty array.
|
|
4011
|
+
*
|
|
4012
|
+
* @param subject - The subject to clone
|
|
4013
|
+
* @param count - How many clones to produce
|
|
4014
|
+
* @returns The `count`-long array of clones
|
|
4015
|
+
*
|
|
4016
|
+
* @example
|
|
4017
|
+
* ```ts
|
|
4018
|
+
* import { repeatSubject } from '@src/core'
|
|
4019
|
+
*
|
|
4020
|
+
* repeatSubject({ id: 's1', age: 30 }, 2) // [{ id: 's1-0', age: 30 }, { id: 's1-1', age: 30 }]
|
|
4021
|
+
* repeatSubject({ age: 30 }, 2) // [{ age: 30 }, { age: 30 }] — no id to mint from
|
|
4022
|
+
* ```
|
|
4023
|
+
*/
|
|
4024
|
+
export declare function repeatSubject(subject: Subject, count: number): readonly Subject[];
|
|
4025
|
+
|
|
4026
|
+
/**
|
|
4027
|
+
* Swap the element sharing `item.id` IN PLACE, preserving its position.
|
|
4028
|
+
*
|
|
4029
|
+
* @remarks
|
|
4030
|
+
* The position-preserving update primitive — unlike {@link appendById}, which
|
|
4031
|
+
* repositions a re-inserted id to the end/target. Appends `item` at the end
|
|
4032
|
+
* when no same-id element exists (never throws).
|
|
4033
|
+
*
|
|
4034
|
+
* @typeParam T - An id-carrying element type
|
|
4035
|
+
* @param items - The collection to update
|
|
4036
|
+
* @param item - The replacement element
|
|
4037
|
+
* @returns A fresh array with the same-id element replaced (or `item` appended)
|
|
4038
|
+
*
|
|
4039
|
+
* @example
|
|
4040
|
+
* ```ts
|
|
4041
|
+
* import { replaceById } from '@src/core'
|
|
4042
|
+
*
|
|
4043
|
+
* replaceById([{ id: 'a', v: 1 }, { id: 'b', v: 2 }], { id: 'a', v: 9 }) // [{a,9}, {b,2}]
|
|
4044
|
+
* replaceById([{ id: 'a' }], { id: 'z' }) // [{a}, {z}] — appended
|
|
4045
|
+
* ```
|
|
4046
|
+
*/
|
|
4047
|
+
export declare function replaceById<T extends {
|
|
4048
|
+
readonly id: string;
|
|
4049
|
+
}>(items: readonly T[], item: T): readonly T[];
|
|
4050
|
+
|
|
4051
|
+
/**
|
|
4052
|
+
* Swap the equation sharing `equation.id` in a {@link SymbolicDefinition} IN
|
|
4053
|
+
* PLACE, preserving its position (appends when absent).
|
|
4054
|
+
*
|
|
4055
|
+
* @param definition - The definition to update
|
|
4056
|
+
* @param source - The replacement equation
|
|
4057
|
+
* @returns A fresh definition with the equation replaced
|
|
4058
|
+
*
|
|
4059
|
+
* @example
|
|
4060
|
+
* ```ts
|
|
4061
|
+
* import { constant, equation, replaceEquation, symbolicDefinition, variable } from '@src/core'
|
|
4062
|
+
*
|
|
4063
|
+
* const definition = symbolicDefinition('e', 'E', [equation('e1', variable('x'), constant(1), 'x')])
|
|
4064
|
+
* replaceEquation(definition, equation('e1', variable('x'), constant(2), 'x'))
|
|
4065
|
+
* ```
|
|
4066
|
+
*/
|
|
4067
|
+
export declare function replaceEquation(definition: SymbolicDefinition, source: Equation): SymbolicDefinition;
|
|
4068
|
+
|
|
4069
|
+
/**
|
|
4070
|
+
* Swap the fact sharing `fact.id` in an {@link InferentialDefinition} IN
|
|
4071
|
+
* PLACE, preserving its position (appends when absent).
|
|
4072
|
+
*
|
|
4073
|
+
* @param definition - The definition to update
|
|
4074
|
+
* @param source - The replacement fact
|
|
4075
|
+
* @returns A fresh definition with the fact replaced
|
|
4076
|
+
*
|
|
4077
|
+
* @example
|
|
4078
|
+
* ```ts
|
|
4079
|
+
* import { fact, inferentialDefinition, replaceFact } from '@src/core'
|
|
4080
|
+
*
|
|
4081
|
+
* const definition = inferentialDefinition('m', 'M', [fact('f1', 'human', ['socrates'])], [])
|
|
4082
|
+
* replaceFact(definition, fact('f1', 'human', ['plato']))
|
|
4083
|
+
* ```
|
|
4084
|
+
*/
|
|
4085
|
+
export declare function replaceFact(definition: InferentialDefinition, source: Fact): InferentialDefinition;
|
|
4086
|
+
|
|
4087
|
+
/**
|
|
4088
|
+
* Swap the factor sharing `factor.id` in a {@link FactorGroup} IN PLACE,
|
|
4089
|
+
* preserving its position (appends when absent).
|
|
4090
|
+
*
|
|
4091
|
+
* @param group - The group to update
|
|
4092
|
+
* @param factor - The replacement factor
|
|
4093
|
+
* @returns A fresh group with the factor replaced
|
|
4094
|
+
*
|
|
4095
|
+
* @example
|
|
4096
|
+
* ```ts
|
|
4097
|
+
* import { factorGroup, replaceFactor, staticFactor } from '@src/core'
|
|
4098
|
+
*
|
|
4099
|
+
* const group = factorGroup('g1', 'sum', [staticFactor('f1', 10)])
|
|
4100
|
+
* replaceFactor(group, staticFactor('f1', 20))
|
|
4101
|
+
* ```
|
|
4102
|
+
*/
|
|
4103
|
+
export declare function replaceFactor(group: FactorGroup, factor: Factor): FactorGroup;
|
|
4104
|
+
|
|
4105
|
+
/**
|
|
4106
|
+
* Swap the group sharing `group.id` in a {@link QuantitativeDefinition} IN
|
|
4107
|
+
* PLACE, preserving its position (appends when absent).
|
|
4108
|
+
*
|
|
4109
|
+
* @param definition - The definition to update
|
|
4110
|
+
* @param group - The replacement group
|
|
4111
|
+
* @returns A fresh definition with the group replaced
|
|
4112
|
+
*
|
|
4113
|
+
* @example
|
|
4114
|
+
* ```ts
|
|
4115
|
+
* import { factorGroup, quantitativeDefinition, replaceGroup } from '@src/core'
|
|
4116
|
+
*
|
|
4117
|
+
* const definition = quantitativeDefinition('risk', 'Risk', [factorGroup('g1', 'sum', [])])
|
|
4118
|
+
* replaceGroup(definition, factorGroup('g1', 'product', []))
|
|
4119
|
+
* ```
|
|
4120
|
+
*/
|
|
4121
|
+
export declare function replaceGroup(definition: QuantitativeDefinition, group: FactorGroup): QuantitativeDefinition;
|
|
4122
|
+
|
|
4123
|
+
/**
|
|
4124
|
+
* Swap the inference sharing `inference.id` in an {@link InferentialDefinition}
|
|
4125
|
+
* IN PLACE, preserving its position (appends when absent).
|
|
4126
|
+
*
|
|
4127
|
+
* @param definition - The definition to update
|
|
4128
|
+
* @param source - The replacement inference
|
|
4129
|
+
* @returns A fresh definition with the inference replaced
|
|
4130
|
+
*
|
|
4131
|
+
* @example
|
|
4132
|
+
* ```ts
|
|
4133
|
+
* import { fact, inference, inferentialDefinition, replaceInference } from '@src/core'
|
|
4134
|
+
*
|
|
4135
|
+
* const original = inference('i1', [fact('p', 'human', ['?x'])], fact('c', 'mortal', ['?x']))
|
|
4136
|
+
* const definition = inferentialDefinition('m', 'M', [], [original])
|
|
4137
|
+
* replaceInference(definition, inference('i1', [], fact('c', 'mortal', ['?x'])))
|
|
4138
|
+
* ```
|
|
4139
|
+
*/
|
|
4140
|
+
export declare function replaceInference(definition: InferentialDefinition, source: Inference): InferentialDefinition;
|
|
4141
|
+
|
|
4142
|
+
/**
|
|
4143
|
+
* Swap the rule sharing `rule.id` in a {@link LogicalDefinition} IN PLACE,
|
|
4144
|
+
* preserving its position (appends when absent).
|
|
4145
|
+
*
|
|
4146
|
+
* @param definition - The definition to update
|
|
4147
|
+
* @param source - The replacement rule
|
|
4148
|
+
* @returns A fresh definition with the rule replaced
|
|
4149
|
+
*
|
|
4150
|
+
* @example
|
|
4151
|
+
* ```ts
|
|
4152
|
+
* import { atom, logicalDefinition, replaceRule, rule } from '@src/core'
|
|
4153
|
+
*
|
|
4154
|
+
* const definition = logicalDefinition('e', 'E', [rule('r1', [], atom('a', 'equals', true))])
|
|
4155
|
+
* replaceRule(definition, rule('r1', [], atom('a', 'equals', false)))
|
|
4156
|
+
* ```
|
|
4157
|
+
*/
|
|
4158
|
+
export declare function replaceRule(definition: LogicalDefinition, source: Rule): LogicalDefinition;
|
|
4159
|
+
|
|
4160
|
+
/**
|
|
4161
|
+
* Round a number to a fixed count of decimal places.
|
|
4162
|
+
*
|
|
4163
|
+
* @remarks
|
|
4164
|
+
* `Math.round` semantics — halves round toward `+∞` (`2.5` → `3`, `-2.5` → `-2`).
|
|
4165
|
+
* A negative precision rounds at whole-number scales (`-1` → tens, `-2` →
|
|
4166
|
+
* hundreds). An EXTREME precision whose scale factor overflows the double range
|
|
4167
|
+
* (`10^p` → `Infinity` at roughly `p > 308`, `0` at roughly `p < -323`) returns
|
|
4168
|
+
* the value UNCHANGED — passthrough, never `NaN`.
|
|
4169
|
+
*
|
|
4170
|
+
* @param value - The number to round
|
|
4171
|
+
* @param precision - Decimal places to keep (defaults to `0`)
|
|
4172
|
+
* @returns The rounded number
|
|
4173
|
+
*
|
|
4174
|
+
* @example
|
|
4175
|
+
* ```ts
|
|
4176
|
+
* import { roundTo } from '@src/core'
|
|
4177
|
+
*
|
|
4178
|
+
* roundTo(3.14159, 2) // 3.14
|
|
4179
|
+
* roundTo(2.5) // 3
|
|
4180
|
+
* roundTo(1250, -2) // 1300 — tens/hundreds scales
|
|
4181
|
+
* roundTo(1.5, 400) // 1.5 — overflow passthrough
|
|
4182
|
+
* ```
|
|
4183
|
+
*/
|
|
4184
|
+
export declare function roundTo(value: number, precision?: number): number;
|
|
4185
|
+
|
|
4186
|
+
/**
|
|
4187
|
+
* One deduction rule: when ALL `premises` hold, the `conclusion`'s atoms are
|
|
4188
|
+
* asserted as derived facts.
|
|
4189
|
+
*
|
|
4190
|
+
* @remarks
|
|
4191
|
+
* `priority` orders evaluation ascending (default `0`, lower runs first);
|
|
4192
|
+
* `enabled: false` skips the rule (omitted from results). Conclusion extraction
|
|
4193
|
+
* ignores connectives — EVERY atom inside the conclusion is asserted as a
|
|
4194
|
+
* `field = value` fact, even under `not` / `or`.
|
|
4195
|
+
*/
|
|
4196
|
+
export declare interface Rule {
|
|
4197
|
+
readonly id: string;
|
|
4198
|
+
readonly name: string;
|
|
4199
|
+
readonly description?: string;
|
|
4200
|
+
readonly premises: readonly Expression[];
|
|
4201
|
+
readonly conclusion: Expression;
|
|
4202
|
+
readonly priority?: number;
|
|
4203
|
+
readonly enabled?: boolean;
|
|
4204
|
+
}
|
|
4205
|
+
|
|
4206
|
+
/**
|
|
4207
|
+
* Build a {@link Rule} — premises and a conclusion.
|
|
4208
|
+
*
|
|
4209
|
+
* @remarks
|
|
4210
|
+
* `name` defaults to the `id`; set `name`, `description`, `priority`, or
|
|
4211
|
+
* `enabled` through `overrides`.
|
|
4212
|
+
*
|
|
4213
|
+
* @param id - The rule id
|
|
4214
|
+
* @param premises - The expressions that must ALL hold
|
|
4215
|
+
* @param conclusion - The expression whose atoms are asserted when they do
|
|
4216
|
+
* @param overrides - Optional {@link Rule} fields merged over the defaults
|
|
4217
|
+
* @returns A fresh rule
|
|
4218
|
+
*
|
|
4219
|
+
* @example
|
|
4220
|
+
* ```ts
|
|
4221
|
+
* import { atom, rule } from '@src/core'
|
|
4222
|
+
*
|
|
4223
|
+
* rule('adult', [atom('age', 'from', 18)], atom('adult', 'equals', true), { priority: 1 })
|
|
4224
|
+
* ```
|
|
4225
|
+
*/
|
|
4226
|
+
export declare function rule(id: string, premises: readonly Expression[], conclusion: Expression, overrides?: Partial<Omit<Rule, 'id' | 'premises' | 'conclusion'>>): Rule;
|
|
4227
|
+
|
|
4228
|
+
/**
|
|
4229
|
+
* The {@link RuleManagerInterface} implementation — a self-owning, kind-free
|
|
4230
|
+
* manager over a logical definition's `rules`.
|
|
4231
|
+
*
|
|
4232
|
+
* @remarks
|
|
4233
|
+
* OWNS its `#rules` collection as private copy-on-write state and its own
|
|
4234
|
+
* {@link Emitter} over {@link RuleManagerEventMap}. Rule order is LOAD-BEARING —
|
|
4235
|
+
* the forward conclusion is the LAST declared non-disabled rule, so `append`
|
|
4236
|
+
* without a `target` makes the new rule the conclusion. The write-only
|
|
4237
|
+
* `collection` setter is the owning builder's silent bulk re-seat channel
|
|
4238
|
+
* (used by `merge`). `destroy()` is idempotent and tears the emitter down LAST;
|
|
4239
|
+
* any other call after it throws `ReasonError('DESTROYED', …)`.
|
|
4240
|
+
*/
|
|
4241
|
+
export declare class RuleManager implements RuleManagerInterface {
|
|
4242
|
+
#private;
|
|
4243
|
+
constructor(options?: RuleManagerOptions);
|
|
4244
|
+
get emitter(): EmitterInterface<RuleManagerEventMap>;
|
|
4245
|
+
set collection(value: readonly Rule[]);
|
|
4246
|
+
rule(id: string): Rule | undefined;
|
|
4247
|
+
rules(): readonly Rule[];
|
|
4248
|
+
append(rule: Rule, target?: string): void;
|
|
4249
|
+
prepend(rule: Rule, target?: string): void;
|
|
4250
|
+
replace(rule: Rule): void;
|
|
4251
|
+
remove(id: string): void;
|
|
4252
|
+
destroy(): void;
|
|
4253
|
+
}
|
|
4254
|
+
|
|
4255
|
+
/** The push observation surface of a {@link RuleManagerInterface} (AGENTS §13). */
|
|
4256
|
+
export declare type RuleManagerEventMap = {
|
|
4257
|
+
/** A rule was appended — carries its id. */
|
|
4258
|
+
readonly append: readonly [id: string];
|
|
4259
|
+
/** A rule was prepended — carries its id. */
|
|
4260
|
+
readonly prepend: readonly [id: string];
|
|
4261
|
+
/** A rule was replaced in place — carries its id. */
|
|
4262
|
+
readonly replace: readonly [id: string];
|
|
4263
|
+
/** A rule was removed — carries its id. */
|
|
4264
|
+
readonly remove: readonly [id: string];
|
|
4265
|
+
/** The manager was destroyed. */
|
|
4266
|
+
readonly destroy: readonly [];
|
|
4267
|
+
};
|
|
4268
|
+
|
|
4269
|
+
/**
|
|
4270
|
+
* The {@link DefinitionBuilderInterface} manager over a logical definition's
|
|
4271
|
+
* `rules` — a self-owning, kind-free collection manager.
|
|
4272
|
+
*
|
|
4273
|
+
* @remarks
|
|
4274
|
+
* Rule order is load-bearing — the forward conclusion is the LAST declared
|
|
4275
|
+
* non-disabled rule, so `append` without a `target` makes a new rule the
|
|
4276
|
+
* conclusion.
|
|
4277
|
+
*/
|
|
4278
|
+
export declare interface RuleManagerInterface {
|
|
4279
|
+
readonly emitter: EmitterInterface<RuleManagerEventMap>;
|
|
4280
|
+
set collection(value: readonly Rule[]);
|
|
4281
|
+
rule(id: string): Rule | undefined;
|
|
4282
|
+
rules(): readonly Rule[];
|
|
4283
|
+
append(rule: Rule, target?: string): void;
|
|
4284
|
+
prepend(rule: Rule, target?: string): void;
|
|
4285
|
+
replace(rule: Rule): void;
|
|
4286
|
+
remove(id: string): void;
|
|
4287
|
+
destroy(): void;
|
|
4288
|
+
}
|
|
4289
|
+
|
|
4290
|
+
/**
|
|
4291
|
+
* Options for `createRuleManager` / the `RuleManager` constructor.
|
|
4292
|
+
*
|
|
4293
|
+
* @remarks
|
|
4294
|
+
* `rules` — the initial collection (defaults to empty). `on` — initial event
|
|
4295
|
+
* listeners (AGENTS §8). `error` — the emitter's listener-error handler
|
|
4296
|
+
* (AGENTS §13).
|
|
4297
|
+
*/
|
|
4298
|
+
export declare interface RuleManagerOptions {
|
|
4299
|
+
readonly rules?: readonly Rule[];
|
|
4300
|
+
readonly on?: EmitterHooks<RuleManagerEventMap>;
|
|
4301
|
+
readonly error?: EmitterErrorHandler;
|
|
4302
|
+
}
|
|
4303
|
+
|
|
4304
|
+
/**
|
|
4305
|
+
* One rule's evaluation outcome.
|
|
4306
|
+
*
|
|
4307
|
+
* @remarks
|
|
4308
|
+
* `applied` and `conclusion` are always equal — both mean "all premises held".
|
|
4309
|
+
* `premises` carries the per-premise truth values.
|
|
4310
|
+
*/
|
|
4311
|
+
export declare interface RuleResult {
|
|
4312
|
+
readonly id: string;
|
|
4313
|
+
readonly applied: boolean;
|
|
4314
|
+
readonly premises: readonly boolean[];
|
|
4315
|
+
readonly conclusion: boolean;
|
|
4316
|
+
}
|
|
4317
|
+
|
|
4318
|
+
/**
|
|
4319
|
+
* Sort items ascending by `priority ?? DEFAULT_PRIORITY` — a stable copy sort.
|
|
4320
|
+
*
|
|
4321
|
+
* @remarks
|
|
4322
|
+
* The shared evaluation-order helper of the quantitative (factors) and logical
|
|
4323
|
+
* (rules) reasoners: lower priorities run first, an absent `priority` defaults
|
|
4324
|
+
* to `0`, equal priorities keep DECLARATION order (stable), and the input array
|
|
4325
|
+
* is never mutated (AGENTS §11). An array hole, `null`, or other non-record
|
|
4326
|
+
* entry is dropped rather than sorted — the output may be shorter than the
|
|
4327
|
+
* input.
|
|
4328
|
+
*
|
|
4329
|
+
* @param items - The priority-carrying items to order
|
|
4330
|
+
* @returns A fresh array, sorted ascending by priority
|
|
4331
|
+
*
|
|
4332
|
+
* @example
|
|
4333
|
+
* ```ts
|
|
4334
|
+
* import { sortByPriority } from '@src/core'
|
|
4335
|
+
*
|
|
4336
|
+
* sortByPriority([{ priority: 5 }, {}, { priority: -1 }])
|
|
4337
|
+
* // [{ priority: -1 }, {}, { priority: 5 }] — default 0 sits between
|
|
4338
|
+
* ```
|
|
4339
|
+
*/
|
|
4340
|
+
export declare function sortByPriority<T extends {
|
|
4341
|
+
readonly priority?: number;
|
|
4342
|
+
}>(items: readonly T[]): readonly T[];
|
|
4343
|
+
|
|
4344
|
+
/** The four factor sources, discriminated by `origin`. */
|
|
4345
|
+
export declare type Source = StaticSource | FieldSource | LookupSource | RangeSource;
|
|
4346
|
+
|
|
4347
|
+
/**
|
|
4348
|
+
* Build a {@link Factor} over a static {@link Source}.
|
|
4349
|
+
*
|
|
4350
|
+
* @remarks
|
|
4351
|
+
* `name` defaults to the `id`; every other {@link Factor} field (checks,
|
|
4352
|
+
* transforms, bounds, weight, priority, enabled, required, fallback) comes
|
|
4353
|
+
* through `overrides`.
|
|
4354
|
+
*
|
|
4355
|
+
* @param id - The factor id
|
|
4356
|
+
* @param value - The fixed source value
|
|
4357
|
+
* @param overrides - Optional {@link Factor} fields merged over the defaults
|
|
4358
|
+
* @returns A fresh factor
|
|
4359
|
+
*
|
|
4360
|
+
* @example
|
|
4361
|
+
* ```ts
|
|
4362
|
+
* import { staticFactor } from '@src/core'
|
|
4363
|
+
*
|
|
4364
|
+
* staticFactor('base-rate', 10, { weight: 2 })
|
|
4365
|
+
* ```
|
|
4366
|
+
*/
|
|
4367
|
+
export declare function staticFactor(id: string, value: number, overrides?: Partial<Omit<Factor, 'id' | 'source'>>): Factor;
|
|
4368
|
+
|
|
4369
|
+
/** A factor source yielding a fixed number. */
|
|
4370
|
+
export declare interface StaticSource {
|
|
4371
|
+
readonly origin: 'static';
|
|
4372
|
+
readonly value: number;
|
|
4373
|
+
}
|
|
4374
|
+
|
|
4375
|
+
/**
|
|
4376
|
+
* Build a static {@link Source} — a fixed number.
|
|
4377
|
+
*
|
|
4378
|
+
* @param value - The fixed value
|
|
4379
|
+
* @returns A fresh static source
|
|
4380
|
+
*
|
|
4381
|
+
* @example
|
|
4382
|
+
* ```ts
|
|
4383
|
+
* import { staticSource } from '@src/core'
|
|
4384
|
+
*
|
|
4385
|
+
* staticSource(42) // { origin: 'static', value: 42 }
|
|
4386
|
+
* ```
|
|
4387
|
+
*/
|
|
4388
|
+
export declare function staticSource(value: number): Source;
|
|
4389
|
+
|
|
4390
|
+
/**
|
|
4391
|
+
* The data record being reasoned about — a plain readonly bag of fields, read
|
|
4392
|
+
* by {@link FieldPath}.
|
|
4393
|
+
*/
|
|
4394
|
+
export declare type Subject = Readonly<Record<string, unknown>>;
|
|
4395
|
+
|
|
4396
|
+
/**
|
|
4397
|
+
* The `SubjectBuilder` entity brand — a `unique symbol` key carrying
|
|
4398
|
+
* `readonly true` on every `SubjectBuilderInterface` instance.
|
|
4399
|
+
*
|
|
4400
|
+
* @remarks
|
|
4401
|
+
* Only `isSubjectBuilder` (`validators.ts`) reads this key, via `Reflect.get`.
|
|
4402
|
+
* Distinct from {@link DEFINITION_BUILDER_BRAND}, so the two entities can never
|
|
4403
|
+
* match each other's guard.
|
|
4404
|
+
*/
|
|
4405
|
+
export declare const SUBJECT_BUILDER_BRAND: unique symbol;
|
|
4406
|
+
|
|
4407
|
+
/**
|
|
4408
|
+
* A stateful workspace builder accumulating a {@link Subject}, taverna
|
|
4409
|
+
* `Workspace`-shaped (AGENTS §4.2.2): a single flat collection, no managers —
|
|
4410
|
+
* a flat sibling of `Reason.ts`.
|
|
4411
|
+
*
|
|
4412
|
+
* @remarks
|
|
4413
|
+
* `id` is OPTIONAL (`options?.id ?? seed.id`). When present, the builder is
|
|
4414
|
+
* id-ful and behaves as before. When absent, the builder is ANONYMOUS —
|
|
4415
|
+
* `.id` is `undefined` and the accumulated subject carries no `id` key.
|
|
4416
|
+
* `field(key)` / `fields()` are the AGENTS §9.1 accessor pair over TOP-LEVEL
|
|
4417
|
+
* keys only. `set(key, value)` delegates to `assignField`; `set('id', …)`
|
|
4418
|
+
* throws `ReasonError('MISMATCH', …)` — id is immutable via the entity,
|
|
4419
|
+
* id-ful or anonymous alike. `remove` is the AGENTS §9.2 batch overload
|
|
4420
|
+
* (array form declared first); removing `'id'` throws the same `MISMATCH`
|
|
4421
|
+
* for the same reason. `merge(incoming)` delegates to `mergeSubjects`
|
|
4422
|
+
* (incoming-wins, base `id` preserved — plain {@link Subject} data only).
|
|
4423
|
+
* `clear()` removes every non-id field, restoring `{ id }` when id-ful or
|
|
4424
|
+
* an empty record when anonymous. `repeat(count)` returns `count`
|
|
4425
|
+
* deterministic minted-id clones as PLAIN payloads — a pure read that does
|
|
4426
|
+
* NOT emit. `build(): Subject` is total, deterministic, and returns a fresh
|
|
4427
|
+
* durable payload each call. Post-destroy mutation throws
|
|
4428
|
+
* `ReasonError('DESTROYED', …)` — only the `emitter` getter and `destroy`
|
|
4429
|
+
* itself keep working, mirroring `Reason`. `destroy()` is idempotent and
|
|
4430
|
+
* tears the emitter down LAST (AGENTS §13).
|
|
4431
|
+
*/
|
|
4432
|
+
export declare class SubjectBuilder implements SubjectBuilderInterface {
|
|
4433
|
+
#private;
|
|
4434
|
+
readonly [SUBJECT_BUILDER_BRAND]: true;
|
|
4435
|
+
constructor(seed: Subject, options?: SubjectBuilderOptions);
|
|
4436
|
+
get id(): string | undefined;
|
|
4437
|
+
get emitter(): EmitterInterface<SubjectBuilderEventMap>;
|
|
4438
|
+
field(key: string): unknown;
|
|
4439
|
+
fields(): Subject;
|
|
4440
|
+
set(key: string, value: unknown): void;
|
|
4441
|
+
remove(keys: readonly string[]): boolean;
|
|
4442
|
+
remove(key: string): boolean;
|
|
4443
|
+
merge(incoming: Subject): void;
|
|
4444
|
+
clear(): void;
|
|
4445
|
+
repeat(count: number): readonly Subject[];
|
|
4446
|
+
build(): Subject;
|
|
4447
|
+
destroy(): void;
|
|
4448
|
+
}
|
|
4449
|
+
|
|
4450
|
+
/**
|
|
4451
|
+
* The push observation surface of a {@link SubjectBuilderInterface} (AGENTS
|
|
4452
|
+
* §13) — five verb-named events, no generic `change` / `status`.
|
|
4453
|
+
*/
|
|
4454
|
+
export declare type SubjectBuilderEventMap = {
|
|
4455
|
+
/** A field was upserted — carries its key and new value. */
|
|
4456
|
+
readonly set: readonly [key: string, value: unknown];
|
|
4457
|
+
/** A field was removed — carries its key. */
|
|
4458
|
+
readonly remove: readonly [key: string];
|
|
4459
|
+
/** The subject was reconciled with an incoming subject — carries the incoming record. */
|
|
4460
|
+
readonly merge: readonly [incoming: Subject];
|
|
4461
|
+
/** Every non-id field was removed. */
|
|
4462
|
+
readonly clear: readonly [];
|
|
4463
|
+
/** The entity was destroyed. */
|
|
4464
|
+
readonly destroy: readonly [];
|
|
4465
|
+
};
|
|
4466
|
+
|
|
4467
|
+
/**
|
|
4468
|
+
* A stateful workspace builder accumulating a {@link Subject}, taverna
|
|
4469
|
+
* `Workspace`-shaped (AGENTS §4.2.2): a single flat collection, no managers.
|
|
4470
|
+
*
|
|
4471
|
+
* @remarks
|
|
4472
|
+
* `id` is OPTIONAL on the entity (`options?.id ?? seed.id`). When present,
|
|
4473
|
+
* the builder is id-ful — `build()`'s output carries that `id` and `clear()`
|
|
4474
|
+
* restores it. When absent, the builder is ANONYMOUS — `.id` is `undefined`,
|
|
4475
|
+
* `build()`'s output carries NO `id` key, and `clear()` empties the record
|
|
4476
|
+
* entirely. `field` / `fields` are the AGENTS §9.1 accessor pair over
|
|
4477
|
+
* TOP-LEVEL keys only. `set(key, value)` delegates to `assignField`;
|
|
4478
|
+
* `set('id', …)` throws — id is immutable via the entity, id-ful or
|
|
4479
|
+
* anonymous alike. `remove` is the AGENTS §9.2 batch overload, array form
|
|
4480
|
+
* declared FIRST. `merge(incoming)` delegates to `mergeSubjects`
|
|
4481
|
+
* (incoming-wins, base `id` preserved — plain {@link Subject} data only).
|
|
4482
|
+
* `clear()` removes every non-id field. `repeat(count)` returns `count`
|
|
4483
|
+
* deterministic minted-id clones as PLAIN payloads — a pure read that does
|
|
4484
|
+
* NOT emit. `build(): Subject` is total, deterministic, and returns a fresh
|
|
4485
|
+
* durable payload each call — distinct from `fields()` (a live inspection
|
|
4486
|
+
* read) even though both currently return the whole record. Post-destroy
|
|
4487
|
+
* mutation throws `ReasonError('DESTROYED', …)`; `destroy()` is idempotent
|
|
4488
|
+
* and tears the emitter down LAST.
|
|
4489
|
+
*/
|
|
4490
|
+
export declare interface SubjectBuilderInterface {
|
|
4491
|
+
readonly [SUBJECT_BUILDER_BRAND]: true;
|
|
4492
|
+
readonly id: string | undefined;
|
|
4493
|
+
readonly emitter: EmitterInterface<SubjectBuilderEventMap>;
|
|
4494
|
+
field(key: string): unknown;
|
|
4495
|
+
fields(): Subject;
|
|
4496
|
+
set(key: string, value: unknown): void;
|
|
4497
|
+
remove(keys: readonly string[]): boolean;
|
|
4498
|
+
remove(key: string): boolean;
|
|
4499
|
+
merge(incoming: Subject): void;
|
|
4500
|
+
clear(): void;
|
|
4501
|
+
repeat(count: number): readonly Subject[];
|
|
4502
|
+
build(): Subject;
|
|
4503
|
+
destroy(): void;
|
|
4504
|
+
}
|
|
4505
|
+
|
|
4506
|
+
/**
|
|
4507
|
+
* Options for `createSubjectBuilder` / the `SubjectBuilder` constructor.
|
|
4508
|
+
*
|
|
4509
|
+
* @remarks
|
|
4510
|
+
* `id` — overrides the seed subject's `id` (defaults to `seed.id`); OPTIONAL
|
|
4511
|
+
* — when neither `options.id` nor a string `seed.id` is present the builder
|
|
4512
|
+
* is ANONYMOUS (`.id` is `undefined`, `build()` emits no `id` key). `on` —
|
|
4513
|
+
* initial event listeners (AGENTS §8). `error` — the emitter's listener-error
|
|
4514
|
+
* handler (AGENTS §13).
|
|
4515
|
+
*/
|
|
4516
|
+
export declare interface SubjectBuilderOptions {
|
|
4517
|
+
readonly id?: string;
|
|
4518
|
+
readonly on?: EmitterHooks<SubjectBuilderEventMap>;
|
|
4519
|
+
readonly error?: EmitterErrorHandler;
|
|
4520
|
+
}
|
|
4521
|
+
|
|
4522
|
+
/**
|
|
4523
|
+
* Project a subject's scalar fields into `has(key, value)` base facts — the
|
|
4524
|
+
* inferential reasoner's subject-injection step.
|
|
4525
|
+
*
|
|
4526
|
+
* @remarks
|
|
4527
|
+
* Every own subject field EXCEPT `id` becomes a `has(key, value)` fact at full
|
|
4528
|
+
* `DEFAULT_CONFIDENCE`; `null` / `undefined` and any `object` (including arrays)
|
|
4529
|
+
* value is skipped. Each injection appends a line to `trace` (mutated), plus a
|
|
4530
|
+
* final count when at least one fact was produced. The injected fact ids are
|
|
4531
|
+
* `subject:<key>`.
|
|
4532
|
+
*
|
|
4533
|
+
* @param subject - The subject to project
|
|
4534
|
+
* @param trace - The trace accumulator to append to (mutated)
|
|
4535
|
+
* @returns The fresh `has(...)` facts (in `Object.keys` order)
|
|
4536
|
+
*
|
|
4537
|
+
* @example
|
|
4538
|
+
* ```ts
|
|
4539
|
+
* import { subjectToFacts } from '@src/core'
|
|
4540
|
+
*
|
|
4541
|
+
* const trace: string[] = []
|
|
4542
|
+
* subjectToFacts({ id: 'p1', age: 42, tags: ['a'] }, trace) // one fact: has('age', 42) — tags skipped
|
|
4543
|
+
* ```
|
|
4544
|
+
*/
|
|
4545
|
+
export declare function subjectToFacts(subject: Subject, trace: string[]): Fact[];
|
|
4546
|
+
|
|
4547
|
+
/** Default `id` for a `SymbolicReasoner`. */
|
|
4548
|
+
export declare const SYMBOLIC_ID = "symbolic";
|
|
4549
|
+
|
|
4550
|
+
/**
|
|
4551
|
+
* A symbolic (equation-solving) definition.
|
|
4552
|
+
*
|
|
4553
|
+
* @remarks
|
|
4554
|
+
* `variables` seeds the bindings; numeric subject fields OVERRIDE same-named
|
|
4555
|
+
* variables. Equations solve strictly in order, each solution rounded to
|
|
4556
|
+
* `precision` decimal places (default `4`) BEFORE feeding forward into later
|
|
4557
|
+
* equations.
|
|
4558
|
+
*/
|
|
4559
|
+
export declare interface SymbolicDefinition {
|
|
4560
|
+
readonly reasoning: 'symbolic';
|
|
4561
|
+
readonly id: string;
|
|
4562
|
+
readonly name: string;
|
|
4563
|
+
readonly description?: string;
|
|
4564
|
+
readonly equations: readonly Equation[];
|
|
4565
|
+
readonly variables: Readonly<Record<string, number>>;
|
|
4566
|
+
readonly precision?: number;
|
|
4567
|
+
}
|
|
4568
|
+
|
|
4569
|
+
/**
|
|
4570
|
+
* Build a {@link SymbolicDefinition}.
|
|
4571
|
+
*
|
|
4572
|
+
* @remarks
|
|
4573
|
+
* `variables` defaults to `{}`; set `variables`, `description`, or `precision`
|
|
4574
|
+
* through `overrides`.
|
|
4575
|
+
*
|
|
4576
|
+
* @param id - The definition id
|
|
4577
|
+
* @param name - The display name
|
|
4578
|
+
* @param equations - The equations, solved in order
|
|
4579
|
+
* @param overrides - Optional {@link SymbolicDefinition} fields merged over the defaults
|
|
4580
|
+
* @returns A fresh symbolic definition
|
|
4581
|
+
*
|
|
4582
|
+
* @example
|
|
4583
|
+
* ```ts
|
|
4584
|
+
* import { constant, equation, symbolicDefinition, variable } from '@src/core'
|
|
4585
|
+
*
|
|
4586
|
+
* symbolicDefinition('rate', 'Rate', [equation('e1', variable('x'), constant(42), 'x')], {
|
|
4587
|
+
* precision: 2,
|
|
4588
|
+
* })
|
|
4589
|
+
* ```
|
|
4590
|
+
*/
|
|
4591
|
+
export declare function symbolicDefinition(id: string, name: string, equations: readonly Equation[], overrides?: Partial<Omit<SymbolicDefinition, 'reasoning' | 'id' | 'name' | 'equations'>>): SymbolicDefinition;
|
|
4592
|
+
|
|
4593
|
+
/** An algebraic expression tree, discriminated by `form`. */
|
|
4594
|
+
export declare type SymbolicExpression = Variable | Constant | Operation;
|
|
4595
|
+
|
|
4596
|
+
/**
|
|
4597
|
+
* The symbolic reasoner — algebraic equation solving by variable isolation.
|
|
4598
|
+
*
|
|
4599
|
+
* @remarks
|
|
4600
|
+
* Bindings seed from `definition.variables`, then numeric subject fields (a
|
|
4601
|
+
* finite number or a numeric string, coerced through the contracts
|
|
4602
|
+
* `parseNumber`) OVERRIDE same-named variables — the `id` field is skipped.
|
|
4603
|
+
* Equations solve strictly in order: when the `target` is unbound and appears
|
|
4604
|
+
* on exactly ONE side, it is isolated by peeling invertible operations (`add` /
|
|
4605
|
+
* `subtract` / `multiply` / `divide`); a non-invertible operation, a target on
|
|
4606
|
+
* both sides of an operation, or an unbound variable throws internally — the
|
|
4607
|
+
* throw is caught PER EQUATION and surfaced as a result error (`Equation
|
|
4608
|
+
* "<id>": <message>` plus a `FAILED` trace) while later equations still run.
|
|
4609
|
+
* Inversion or division by zero yields `NaN`, caught by the non-finite check.
|
|
4610
|
+
* A solved value is rounded to `precision` BEFORE binding, so later equations
|
|
4611
|
+
* see the rounded value; `solutions` reads FINAL bindings keyed by each
|
|
4612
|
+
* equation's target (a failed equation's target still appears when bound
|
|
4613
|
+
* elsewhere). Nothing mutates its inputs; fully deterministic (AGENTS §11).
|
|
4614
|
+
*/
|
|
4615
|
+
export declare class SymbolicReasoner implements ReasonerInterface {
|
|
4616
|
+
#private;
|
|
4617
|
+
constructor(options?: SymbolicReasonerOptions);
|
|
4618
|
+
get id(): string;
|
|
4619
|
+
get reasoning(): Reasoning;
|
|
4620
|
+
supports(definition: Definition): boolean;
|
|
4621
|
+
validate(definition: Definition): ReasonValidationResult;
|
|
4622
|
+
reason(subject: Subject, definition: Definition): ReasonResult;
|
|
4623
|
+
}
|
|
4624
|
+
|
|
4625
|
+
/**
|
|
4626
|
+
* Options for `createSymbolicReasoner` / the `SymbolicReasoner` constructor.
|
|
4627
|
+
*
|
|
4628
|
+
* @remarks
|
|
4629
|
+
* `id` — the reasoner's identity string (defaults to `SYMBOLIC_ID`).
|
|
4630
|
+
*/
|
|
4631
|
+
export declare interface SymbolicReasonerOptions {
|
|
4632
|
+
readonly id?: string;
|
|
4633
|
+
}
|
|
4634
|
+
|
|
4635
|
+
/**
|
|
4636
|
+
* The outcome of symbolic reasoning — final bindings keyed by each equation's
|
|
4637
|
+
* `target` (a failed equation's target still appears when bound elsewhere).
|
|
4638
|
+
*/
|
|
4639
|
+
export declare interface SymbolicResult {
|
|
4640
|
+
readonly reasoning: 'symbolic';
|
|
4641
|
+
readonly solutions: Readonly<Record<string, number>>;
|
|
4642
|
+
readonly success: boolean;
|
|
4643
|
+
readonly trace: readonly string[];
|
|
4644
|
+
readonly errors: readonly string[];
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4647
|
+
/**
|
|
4648
|
+
* Derive one fact term's contribution to a dedup key — reference identity for
|
|
4649
|
+
* non-null objects / functions, a SameValueZero value string for primitives.
|
|
4650
|
+
*
|
|
4651
|
+
* @remarks
|
|
4652
|
+
* The per-term half of {@link factToKey}, used by the inferential reasoner's
|
|
4653
|
+
* forward-chaining dedupe. Primitives (and `null`) key by value, typeof-prefixed
|
|
4654
|
+
* so `1` (`number:1`) never collides with `'1'` (`string:1`); `-0` folds to `+0`
|
|
4655
|
+
* (both `number:0`) and `NaN` is self-consistent (`number:NaN`), matching
|
|
4656
|
+
* SameValueZero. Objects and functions key by REFERENCE through `identities` — a
|
|
4657
|
+
* first sighting is assigned the map's current size as its id, so distinct
|
|
4658
|
+
* objects never collide and the SAME reference always reproduces its key.
|
|
4659
|
+
*
|
|
4660
|
+
* @param term - The term to key
|
|
4661
|
+
* @param identities - The reference-identity map, threaded across a dedupe pass (mutated: a new object/function is registered)
|
|
4662
|
+
* @returns The term's key string
|
|
4663
|
+
*
|
|
4664
|
+
* @example
|
|
4665
|
+
* ```ts
|
|
4666
|
+
* import { termToKey } from '@src/core'
|
|
4667
|
+
*
|
|
4668
|
+
* const identities = new Map<object, number>()
|
|
4669
|
+
* termToKey(1, identities) // 'number:1'
|
|
4670
|
+
* termToKey('1', identities) // 'string:1' — never collides with the number
|
|
4671
|
+
* ```
|
|
4672
|
+
*/
|
|
4673
|
+
export declare function termToKey(term: unknown, identities: Map<object, number>): string;
|
|
4674
|
+
|
|
4675
|
+
/**
|
|
4676
|
+
* One math step applied to a number by the {@link TransformerInterface}.
|
|
4677
|
+
*
|
|
4678
|
+
* @remarks
|
|
4679
|
+
* The absent-`operand` default is operation-specific: `1` for `multiply` /
|
|
4680
|
+
* `divide` / `power` (identity-preserving), `0` for every other binary
|
|
4681
|
+
* operation; unary operations ignore it.
|
|
4682
|
+
*/
|
|
4683
|
+
export declare interface Transform {
|
|
4684
|
+
readonly operation: MathOperation;
|
|
4685
|
+
readonly operand?: number;
|
|
4686
|
+
}
|
|
4687
|
+
|
|
4688
|
+
/**
|
|
4689
|
+
* Build a {@link Transform} — one math step.
|
|
4690
|
+
*
|
|
4691
|
+
* @remarks
|
|
4692
|
+
* The `operand` key is OMITTED when absent (never set to `undefined`), so the
|
|
4693
|
+
* transform stays exact-record valid; the transformer then applies its
|
|
4694
|
+
* per-operation default (`1` for `multiply` / `divide` / `power`, `0` otherwise).
|
|
4695
|
+
*
|
|
4696
|
+
* @param operation - The math operation to apply
|
|
4697
|
+
* @param operand - The operand (ignored by the unary operations)
|
|
4698
|
+
* @returns A fresh transform
|
|
4699
|
+
*
|
|
4700
|
+
* @example
|
|
4701
|
+
* ```ts
|
|
4702
|
+
* import { transform } from '@src/core'
|
|
4703
|
+
*
|
|
4704
|
+
* transform('multiply', 2) // { operation: 'multiply', operand: 2 }
|
|
4705
|
+
* transform('round') // { operation: 'round' }
|
|
4706
|
+
* ```
|
|
4707
|
+
*/
|
|
4708
|
+
export declare function transform(operation: MathOperation, operand?: number): Transform;
|
|
4709
|
+
|
|
4710
|
+
/**
|
|
4711
|
+
* Applies math {@link Transform}s to numbers — the quantitative reasoner's
|
|
4712
|
+
* per-factor pipeline stage.
|
|
4713
|
+
*
|
|
4714
|
+
* @remarks
|
|
4715
|
+
* TOTAL: never throws. Absent-`operand` defaults are operation-specific —
|
|
4716
|
+
* identity-preserving `1` for `multiply` / `divide` / `power`, `0` for every
|
|
4717
|
+
* other binary operation; `round` / `ceil` / `floor` / `abs` are unary and
|
|
4718
|
+
* ignore the operand. `divide` by zero yields `NaN` (deliberately not JS's
|
|
4719
|
+
* `Infinity`), an unknown operation returns the value unchanged, and `chain` is
|
|
4720
|
+
* a strict left fold — `NaN` flows through untouched. Stateless and
|
|
4721
|
+
* deterministic.
|
|
4722
|
+
*/
|
|
4723
|
+
export declare class Transformer implements TransformerInterface {
|
|
4724
|
+
#private;
|
|
4725
|
+
constructor(options?: TransformerOptions);
|
|
4726
|
+
get id(): string;
|
|
4727
|
+
apply(value: number, transform: Transform): number;
|
|
4728
|
+
chain(value: number, transforms: readonly Transform[]): number;
|
|
4729
|
+
}
|
|
4730
|
+
|
|
4731
|
+
/** Default `id` for a `Transformer`. */
|
|
4732
|
+
export declare const TRANSFORMER_ID = "transformer";
|
|
4733
|
+
|
|
4734
|
+
/**
|
|
4735
|
+
* Applies math {@link Transform}s to numbers.
|
|
4736
|
+
*
|
|
4737
|
+
* @remarks
|
|
4738
|
+
* Total: an unknown operation returns the value unchanged, and `divide` by
|
|
4739
|
+
* zero yields `NaN` rather than throwing. `chain` is a strict left fold —
|
|
4740
|
+
* `NaN` flows through.
|
|
4741
|
+
*/
|
|
4742
|
+
export declare interface TransformerInterface {
|
|
4743
|
+
readonly id: string;
|
|
4744
|
+
apply(value: number, transform: Transform): number;
|
|
4745
|
+
chain(value: number, transforms: readonly Transform[]): number;
|
|
4746
|
+
}
|
|
4747
|
+
|
|
4748
|
+
/**
|
|
4749
|
+
* Options for `createTransformer` / the `Transformer` constructor.
|
|
4750
|
+
*
|
|
4751
|
+
* @remarks
|
|
4752
|
+
* `id` — the transformer's identity string (defaults to `TRANSFORMER_ID`).
|
|
4753
|
+
*/
|
|
4754
|
+
export declare interface TransformerOptions {
|
|
4755
|
+
readonly id?: string;
|
|
4756
|
+
}
|
|
4757
|
+
|
|
4758
|
+
/** A symbolic expression leaf naming a variable. */
|
|
4759
|
+
export declare interface Variable {
|
|
4760
|
+
readonly form: 'variable';
|
|
4761
|
+
readonly name: string;
|
|
4762
|
+
}
|
|
4763
|
+
|
|
4764
|
+
/**
|
|
4765
|
+
* Build a variable {@link SymbolicExpression} leaf.
|
|
4766
|
+
*
|
|
4767
|
+
* @param name - The variable name
|
|
4768
|
+
* @returns A fresh variable node
|
|
4769
|
+
*
|
|
4770
|
+
* @example
|
|
4771
|
+
* ```ts
|
|
4772
|
+
* import { variable } from '@src/core'
|
|
4773
|
+
*
|
|
4774
|
+
* variable('x') // { form: 'variable', name: 'x' }
|
|
4775
|
+
* ```
|
|
4776
|
+
*/
|
|
4777
|
+
export declare function variable(name: string): SymbolicExpression;
|
|
4778
|
+
|
|
4779
|
+
/**
|
|
4780
|
+
* The {@link VariableManagerInterface} implementation — a self-owning,
|
|
4781
|
+
* kind-free manager over a symbolic definition's `variables`, a name-keyed
|
|
4782
|
+
* unordered record.
|
|
4783
|
+
*
|
|
4784
|
+
* @remarks
|
|
4785
|
+
* OWNS its `#variables` record as private copy-on-write state and its own
|
|
4786
|
+
* {@link Emitter} over {@link VariableManagerEventMap}. The record has no
|
|
4787
|
+
* placement, so only `add` / `remove` exist (no `append` / `prepend`): `add`
|
|
4788
|
+
* upserts and emits `add(name)`, `remove` omits the key entirely (never sets
|
|
4789
|
+
* `undefined`) and emits `remove(name)`. The write-only `collection` setter is
|
|
4790
|
+
* the owning builder's silent bulk re-seat channel (used by `merge`).
|
|
4791
|
+
* `destroy()` is idempotent and tears the emitter down LAST; any other call
|
|
4792
|
+
* after it throws `ReasonError('DESTROYED', …)`.
|
|
4793
|
+
*/
|
|
4794
|
+
export declare class VariableManager implements VariableManagerInterface {
|
|
4795
|
+
#private;
|
|
4796
|
+
constructor(options?: VariableManagerOptions);
|
|
4797
|
+
get emitter(): EmitterInterface<VariableManagerEventMap>;
|
|
4798
|
+
set collection(value: Readonly<Record<string, number>>);
|
|
4799
|
+
variable(name: string): number | undefined;
|
|
4800
|
+
variables(): Readonly<Record<string, number>>;
|
|
4801
|
+
add(name: string, value: number): void;
|
|
4802
|
+
remove(name: string): void;
|
|
4803
|
+
destroy(): void;
|
|
4804
|
+
}
|
|
4805
|
+
|
|
4806
|
+
/**
|
|
4807
|
+
* The push observation surface of a {@link VariableManagerInterface}
|
|
4808
|
+
* (AGENTS §13).
|
|
4809
|
+
*
|
|
4810
|
+
* @remarks
|
|
4811
|
+
* `variables` is a name-keyed record with no placement, so the honest verbs
|
|
4812
|
+
* are `add` / `remove` — each carries the variable NAME.
|
|
4813
|
+
*/
|
|
4814
|
+
export declare type VariableManagerEventMap = {
|
|
4815
|
+
/** A variable was upserted — carries its name. */
|
|
4816
|
+
readonly add: readonly [name: string];
|
|
4817
|
+
/** A variable was removed — carries its name. */
|
|
4818
|
+
readonly remove: readonly [name: string];
|
|
4819
|
+
/** The manager was destroyed. */
|
|
4820
|
+
readonly destroy: readonly [];
|
|
4821
|
+
};
|
|
4822
|
+
|
|
4823
|
+
/**
|
|
4824
|
+
* The {@link DefinitionBuilderInterface} manager over a symbolic definition's
|
|
4825
|
+
* `variables` — a name-keyed unordered record, so `add` / `remove` are the
|
|
4826
|
+
* only write verbs (no placement). A self-owning, kind-free manager.
|
|
4827
|
+
*/
|
|
4828
|
+
export declare interface VariableManagerInterface {
|
|
4829
|
+
readonly emitter: EmitterInterface<VariableManagerEventMap>;
|
|
4830
|
+
set collection(value: Readonly<Record<string, number>>);
|
|
4831
|
+
variable(name: string): number | undefined;
|
|
4832
|
+
variables(): Readonly<Record<string, number>>;
|
|
4833
|
+
add(name: string, value: number): void;
|
|
4834
|
+
remove(name: string): void;
|
|
4835
|
+
destroy(): void;
|
|
4836
|
+
}
|
|
4837
|
+
|
|
4838
|
+
/**
|
|
4839
|
+
* Options for `createVariableManager` / the `VariableManager` constructor.
|
|
4840
|
+
*
|
|
4841
|
+
* @remarks
|
|
4842
|
+
* `variables` — the initial record (defaults to empty). `on` — initial event
|
|
4843
|
+
* listeners (AGENTS §8). `error` — the emitter's listener-error handler
|
|
4844
|
+
* (AGENTS §13).
|
|
4845
|
+
*/
|
|
4846
|
+
export declare interface VariableManagerOptions {
|
|
4847
|
+
readonly variables?: Readonly<Record<string, number>>;
|
|
4848
|
+
readonly on?: EmitterHooks<VariableManagerEventMap>;
|
|
4849
|
+
readonly error?: EmitterErrorHandler;
|
|
4850
|
+
}
|
|
4851
|
+
|
|
4852
|
+
export { }
|