@plures/praxis 1.2.41 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/{chunk-BBP2F7TT.js → chunk-MJK3IYTJ.js} +123 -5
- package/dist/browser/{chunk-FCEH7WMH.js → chunk-N63K4KWS.js} +1 -1
- package/dist/browser/{engine-65QDGCAN.js → engine-YIEGSX7U.js} +1 -1
- package/dist/browser/index.d.ts +2 -2
- package/dist/browser/index.js +10 -5
- package/dist/browser/integrations/svelte.d.ts +2 -2
- package/dist/browser/integrations/svelte.js +2 -2
- package/dist/browser/{reactive-engine.svelte-Cqd8Mod2.d.ts → reactive-engine.svelte-DjynI82A.d.ts} +83 -4
- package/dist/node/{chunk-32YFEEML.js → chunk-5JQJZADT.js} +1 -1
- package/dist/node/{chunk-BBP2F7TT.js → chunk-KMJWAFZV.js} +128 -5
- package/dist/node/cloud/index.d.cts +1 -1
- package/dist/node/cloud/index.d.ts +1 -1
- package/dist/node/{engine-7CXQV6RC.js → engine-FEN5IYZ5.js} +1 -1
- package/dist/node/index.cjs +522 -59
- package/dist/node/index.d.cts +271 -5
- package/dist/node/index.d.ts +271 -5
- package/dist/node/index.js +355 -39
- package/dist/node/integrations/svelte.cjs +123 -5
- package/dist/node/integrations/svelte.d.cts +3 -3
- package/dist/node/integrations/svelte.d.ts +3 -3
- package/dist/node/integrations/svelte.js +2 -2
- package/dist/node/{protocol-BocKczNv.d.ts → protocol-DcyGMmWY.d.cts} +7 -0
- package/dist/node/{protocol-BocKczNv.d.cts → protocol-DcyGMmWY.d.ts} +7 -0
- package/dist/node/{reactive-engine.svelte-D-xTDxT5.d.ts → reactive-engine.svelte-Cg0Yc2Hs.d.cts} +90 -6
- package/dist/node/{reactive-engine.svelte-CGe8SpVE.d.cts → reactive-engine.svelte-DekxqFu0.d.ts} +90 -6
- package/package.json +2 -2
- package/src/__tests__/engine-v2.test.ts +532 -0
- package/src/core/completeness.ts +274 -0
- package/src/core/engine.ts +47 -5
- package/src/core/pluresdb/store.ts +9 -3
- package/src/core/protocol.ts +7 -0
- package/src/core/rule-result.ts +130 -0
- package/src/core/rules.ts +12 -5
- package/src/core/ui-rules.ts +340 -0
- package/src/index.ts +27 -0
- package/src/vite/completeness-plugin.ts +72 -0
|
@@ -196,6 +196,90 @@ var PraxisRegistry = class {
|
|
|
196
196
|
// src/core/protocol.ts
|
|
197
197
|
var PRAXIS_PROTOCOL_VERSION = "1.0.0";
|
|
198
198
|
|
|
199
|
+
// src/core/rule-result.ts
|
|
200
|
+
var RuleResult = class _RuleResult {
|
|
201
|
+
/** The kind of result */
|
|
202
|
+
kind;
|
|
203
|
+
/** Facts produced (only for 'emit') */
|
|
204
|
+
facts;
|
|
205
|
+
/** Fact tags to retract (only for 'retract') */
|
|
206
|
+
retractTags;
|
|
207
|
+
/** Optional reason (for noop/skip/retract — useful for debugging) */
|
|
208
|
+
reason;
|
|
209
|
+
/** The rule ID that produced this result (set by engine) */
|
|
210
|
+
ruleId;
|
|
211
|
+
constructor(kind, facts, retractTags, reason) {
|
|
212
|
+
this.kind = kind;
|
|
213
|
+
this.facts = facts;
|
|
214
|
+
this.retractTags = retractTags;
|
|
215
|
+
this.reason = reason;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Rule produced facts.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* return RuleResult.emit([
|
|
222
|
+
* { tag: 'sprint.behind', payload: { deficit: 5 } }
|
|
223
|
+
* ]);
|
|
224
|
+
*/
|
|
225
|
+
static emit(facts) {
|
|
226
|
+
if (facts.length === 0) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
"RuleResult.emit() requires at least one fact. Use RuleResult.noop() or RuleResult.skip() when a rule has nothing to say."
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
return new _RuleResult("emit", facts, []);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Rule evaluated but had nothing to report.
|
|
235
|
+
* Unlike returning [], this is explicit and traceable.
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* if (ctx.completedHours >= expectedHours) {
|
|
239
|
+
* return RuleResult.noop('Sprint is on pace');
|
|
240
|
+
* }
|
|
241
|
+
*/
|
|
242
|
+
static noop(reason) {
|
|
243
|
+
return new _RuleResult("noop", [], [], reason);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Rule decided to skip because preconditions were not met.
|
|
247
|
+
* Distinct from noop: skip means "I can't evaluate", noop means "I evaluated and found nothing".
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* if (!ctx.sprintName) {
|
|
251
|
+
* return RuleResult.skip('No active sprint');
|
|
252
|
+
* }
|
|
253
|
+
*/
|
|
254
|
+
static skip(reason) {
|
|
255
|
+
return new _RuleResult("skip", [], [], reason);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Rule retracts previously emitted facts by tag.
|
|
259
|
+
* Used when a condition that previously produced facts is no longer true.
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* // Sprint was behind, but caught up
|
|
263
|
+
* if (ctx.completedHours >= expectedHours) {
|
|
264
|
+
* return RuleResult.retract(['sprint.behind'], 'Sprint caught up');
|
|
265
|
+
* }
|
|
266
|
+
*/
|
|
267
|
+
static retract(tags, reason) {
|
|
268
|
+
if (tags.length === 0) {
|
|
269
|
+
throw new Error("RuleResult.retract() requires at least one tag.");
|
|
270
|
+
}
|
|
271
|
+
return new _RuleResult("retract", [], tags, reason);
|
|
272
|
+
}
|
|
273
|
+
/** Whether this result produced facts */
|
|
274
|
+
get hasFacts() {
|
|
275
|
+
return this.facts.length > 0;
|
|
276
|
+
}
|
|
277
|
+
/** Whether this result retracts facts */
|
|
278
|
+
get hasRetractions() {
|
|
279
|
+
return this.retractTags.length > 0;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
199
283
|
// src/core/engine.ts
|
|
200
284
|
function safeClone(value) {
|
|
201
285
|
if (value === null || typeof value !== "object") {
|
|
@@ -275,7 +359,13 @@ var LogicEngine = class {
|
|
|
275
359
|
stepWithConfig(events, config) {
|
|
276
360
|
const diagnostics = [];
|
|
277
361
|
let newState = { ...this.state };
|
|
362
|
+
const stateWithEvents = {
|
|
363
|
+
...newState,
|
|
364
|
+
events
|
|
365
|
+
// current batch — rules can read state.events
|
|
366
|
+
};
|
|
278
367
|
const newFacts = [];
|
|
368
|
+
const retractions = [];
|
|
279
369
|
const eventTags = new Set(events.map((e) => e.tag));
|
|
280
370
|
for (const ruleId of config.ruleIds) {
|
|
281
371
|
const rule = this.registry.getRule(ruleId);
|
|
@@ -294,8 +384,31 @@ var LogicEngine = class {
|
|
|
294
384
|
}
|
|
295
385
|
}
|
|
296
386
|
try {
|
|
297
|
-
const
|
|
298
|
-
|
|
387
|
+
const rawResult = rule.impl(stateWithEvents, events);
|
|
388
|
+
if (rawResult instanceof RuleResult) {
|
|
389
|
+
rawResult.ruleId = ruleId;
|
|
390
|
+
switch (rawResult.kind) {
|
|
391
|
+
case "emit":
|
|
392
|
+
newFacts.push(...rawResult.facts);
|
|
393
|
+
break;
|
|
394
|
+
case "retract":
|
|
395
|
+
retractions.push(...rawResult.retractTags);
|
|
396
|
+
break;
|
|
397
|
+
case "noop":
|
|
398
|
+
case "skip":
|
|
399
|
+
if (rawResult.reason) {
|
|
400
|
+
diagnostics.push({
|
|
401
|
+
kind: "rule-error",
|
|
402
|
+
// reused kind — could add 'rule-trace' in protocol v2
|
|
403
|
+
message: `[${rawResult.kind}] ${ruleId}: ${rawResult.reason}`,
|
|
404
|
+
data: { ruleId, resultKind: rawResult.kind, reason: rawResult.reason }
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
} else if (Array.isArray(rawResult)) {
|
|
410
|
+
newFacts.push(...rawResult);
|
|
411
|
+
}
|
|
299
412
|
} catch (error) {
|
|
300
413
|
diagnostics.push({
|
|
301
414
|
kind: "rule-error",
|
|
@@ -304,21 +417,26 @@ var LogicEngine = class {
|
|
|
304
417
|
});
|
|
305
418
|
}
|
|
306
419
|
}
|
|
420
|
+
let existingFacts = newState.facts;
|
|
421
|
+
if (retractions.length > 0) {
|
|
422
|
+
const retractSet = new Set(retractions);
|
|
423
|
+
existingFacts = existingFacts.filter((f) => !retractSet.has(f.tag));
|
|
424
|
+
}
|
|
307
425
|
let mergedFacts;
|
|
308
426
|
switch (this.factDedup) {
|
|
309
427
|
case "last-write-wins": {
|
|
310
428
|
const factMap = /* @__PURE__ */ new Map();
|
|
311
|
-
for (const f of
|
|
429
|
+
for (const f of existingFacts) factMap.set(f.tag, f);
|
|
312
430
|
for (const f of newFacts) factMap.set(f.tag, f);
|
|
313
431
|
mergedFacts = Array.from(factMap.values());
|
|
314
432
|
break;
|
|
315
433
|
}
|
|
316
434
|
case "append":
|
|
317
|
-
mergedFacts = [...
|
|
435
|
+
mergedFacts = [...existingFacts, ...newFacts];
|
|
318
436
|
break;
|
|
319
437
|
case "none":
|
|
320
438
|
default:
|
|
321
|
-
mergedFacts = [...
|
|
439
|
+
mergedFacts = [...existingFacts, ...newFacts];
|
|
322
440
|
break;
|
|
323
441
|
}
|
|
324
442
|
if (this.maxFacts > 0 && mergedFacts.length > this.maxFacts) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { L as LogicEngine } from '../reactive-engine.svelte-
|
|
2
|
-
export { R as ReactiveEngineOptions, a as ReactiveLogicEngine, c as createReactiveEngine } from '../reactive-engine.svelte-
|
|
3
|
-
import { P as PraxisState, a as PraxisEvent } from '../protocol-
|
|
1
|
+
import { L as LogicEngine } from '../reactive-engine.svelte-Cg0Yc2Hs.cjs';
|
|
2
|
+
export { R as ReactiveEngineOptions, a as ReactiveLogicEngine, c as createReactiveEngine } from '../reactive-engine.svelte-Cg0Yc2Hs.cjs';
|
|
3
|
+
import { P as PraxisState, a as PraxisEvent } from '../protocol-DcyGMmWY.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Svelte v5 Integration
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { L as LogicEngine } from '../reactive-engine.svelte-
|
|
2
|
-
export { R as ReactiveEngineOptions, a as ReactiveLogicEngine, c as createReactiveEngine } from '../reactive-engine.svelte-
|
|
3
|
-
import { P as PraxisState, a as PraxisEvent } from '../protocol-
|
|
1
|
+
import { L as LogicEngine } from '../reactive-engine.svelte-DekxqFu0.js';
|
|
2
|
+
export { R as ReactiveEngineOptions, a as ReactiveLogicEngine, c as createReactiveEngine } from '../reactive-engine.svelte-DekxqFu0.js';
|
|
3
|
+
import { P as PraxisState, a as PraxisEvent } from '../protocol-DcyGMmWY.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Svelte v5 Integration
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ReactiveLogicEngine,
|
|
3
3
|
createReactiveEngine
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-5JQJZADT.js";
|
|
5
5
|
import "../chunk-R2PSBPKQ.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-KMJWAFZV.js";
|
|
7
7
|
import "../chunk-QGM4M3NI.js";
|
|
8
8
|
|
|
9
9
|
// src/integrations/svelte.ts
|
|
@@ -71,6 +71,13 @@ interface PraxisState {
|
|
|
71
71
|
context: unknown;
|
|
72
72
|
/** Current facts about the domain */
|
|
73
73
|
facts: PraxisFact[];
|
|
74
|
+
/**
|
|
75
|
+
* Events currently being processed in this step.
|
|
76
|
+
* Available to rules during execution — guaranteed to contain the exact
|
|
77
|
+
* events passed to step()/stepWithContext().
|
|
78
|
+
* Empty outside of step execution.
|
|
79
|
+
*/
|
|
80
|
+
events?: PraxisEvent[];
|
|
74
81
|
/** Optional metadata (timestamps, version, etc.) */
|
|
75
82
|
meta?: Record<string, unknown>;
|
|
76
83
|
/** Protocol version (for cross-language compatibility) */
|
|
@@ -71,6 +71,13 @@ interface PraxisState {
|
|
|
71
71
|
context: unknown;
|
|
72
72
|
/** Current facts about the domain */
|
|
73
73
|
facts: PraxisFact[];
|
|
74
|
+
/**
|
|
75
|
+
* Events currently being processed in this step.
|
|
76
|
+
* Available to rules during execution — guaranteed to contain the exact
|
|
77
|
+
* events passed to step()/stepWithContext().
|
|
78
|
+
* Empty outside of step execution.
|
|
79
|
+
*/
|
|
80
|
+
events?: PraxisEvent[];
|
|
74
81
|
/** Optional metadata (timestamps, version, etc.) */
|
|
75
82
|
meta?: Record<string, unknown>;
|
|
76
83
|
/** Protocol version (for cross-language compatibility) */
|
package/dist/node/{reactive-engine.svelte-D-xTDxT5.d.ts → reactive-engine.svelte-Cg0Yc2Hs.d.cts}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as PraxisFact, P as PraxisState, a as PraxisEvent, c as PraxisStepResult, d as PraxisStepConfig, e as PraxisDiagnostics } from './protocol-DcyGMmWY.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Decision Ledger - Contract Types
|
|
@@ -162,6 +162,83 @@ interface ValidationReport {
|
|
|
162
162
|
timestamp: string;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/**
|
|
166
|
+
* The result of evaluating a rule. Every rule MUST return one of:
|
|
167
|
+
* - `RuleResult.emit(facts)` — rule produced facts
|
|
168
|
+
* - `RuleResult.noop(reason?)` — rule evaluated but had nothing to say
|
|
169
|
+
* - `RuleResult.skip(reason?)` — rule decided to skip (preconditions not met)
|
|
170
|
+
* - `RuleResult.retract(tags)` — rule retracts previously emitted facts
|
|
171
|
+
*/
|
|
172
|
+
declare class RuleResult {
|
|
173
|
+
/** The kind of result */
|
|
174
|
+
readonly kind: 'emit' | 'noop' | 'skip' | 'retract';
|
|
175
|
+
/** Facts produced (only for 'emit') */
|
|
176
|
+
readonly facts: PraxisFact[];
|
|
177
|
+
/** Fact tags to retract (only for 'retract') */
|
|
178
|
+
readonly retractTags: string[];
|
|
179
|
+
/** Optional reason (for noop/skip/retract — useful for debugging) */
|
|
180
|
+
readonly reason?: string;
|
|
181
|
+
/** The rule ID that produced this result (set by engine) */
|
|
182
|
+
ruleId?: string;
|
|
183
|
+
private constructor();
|
|
184
|
+
/**
|
|
185
|
+
* Rule produced facts.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* return RuleResult.emit([
|
|
189
|
+
* { tag: 'sprint.behind', payload: { deficit: 5 } }
|
|
190
|
+
* ]);
|
|
191
|
+
*/
|
|
192
|
+
static emit(facts: PraxisFact[]): RuleResult;
|
|
193
|
+
/**
|
|
194
|
+
* Rule evaluated but had nothing to report.
|
|
195
|
+
* Unlike returning [], this is explicit and traceable.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* if (ctx.completedHours >= expectedHours) {
|
|
199
|
+
* return RuleResult.noop('Sprint is on pace');
|
|
200
|
+
* }
|
|
201
|
+
*/
|
|
202
|
+
static noop(reason?: string): RuleResult;
|
|
203
|
+
/**
|
|
204
|
+
* Rule decided to skip because preconditions were not met.
|
|
205
|
+
* Distinct from noop: skip means "I can't evaluate", noop means "I evaluated and found nothing".
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* if (!ctx.sprintName) {
|
|
209
|
+
* return RuleResult.skip('No active sprint');
|
|
210
|
+
* }
|
|
211
|
+
*/
|
|
212
|
+
static skip(reason?: string): RuleResult;
|
|
213
|
+
/**
|
|
214
|
+
* Rule retracts previously emitted facts by tag.
|
|
215
|
+
* Used when a condition that previously produced facts is no longer true.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* // Sprint was behind, but caught up
|
|
219
|
+
* if (ctx.completedHours >= expectedHours) {
|
|
220
|
+
* return RuleResult.retract(['sprint.behind'], 'Sprint caught up');
|
|
221
|
+
* }
|
|
222
|
+
*/
|
|
223
|
+
static retract(tags: string[], reason?: string): RuleResult;
|
|
224
|
+
/** Whether this result produced facts */
|
|
225
|
+
get hasFacts(): boolean;
|
|
226
|
+
/** Whether this result retracts facts */
|
|
227
|
+
get hasRetractions(): boolean;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* A rule function that returns a typed RuleResult.
|
|
231
|
+
* New API — replaces the old PraxisFact[] return type.
|
|
232
|
+
*/
|
|
233
|
+
type TypedRuleFn<TContext = unknown> = (state: PraxisState & {
|
|
234
|
+
context: TContext;
|
|
235
|
+
events: PraxisEvent[];
|
|
236
|
+
}, events: PraxisEvent[]) => RuleResult;
|
|
237
|
+
/**
|
|
238
|
+
* Convenience: create a fact object (just a shorthand)
|
|
239
|
+
*/
|
|
240
|
+
declare function fact(tag: string, payload: unknown): PraxisFact;
|
|
241
|
+
|
|
165
242
|
/**
|
|
166
243
|
* Rules and Constraints System
|
|
167
244
|
*
|
|
@@ -182,13 +259,20 @@ type ConstraintId = string;
|
|
|
182
259
|
* A rule function derives new facts or transitions from context + input facts/events.
|
|
183
260
|
* Rules must be pure - no side effects.
|
|
184
261
|
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
262
|
+
* Returns either:
|
|
263
|
+
* - `RuleResult` (new API — typed, traceable, supports retraction)
|
|
264
|
+
* - `PraxisFact[]` (legacy — backward compatible, will be deprecated)
|
|
265
|
+
*
|
|
266
|
+
* The state parameter includes `events` — the current batch being processed.
|
|
267
|
+
*
|
|
268
|
+
* @param state Current Praxis state (includes state.events for current batch)
|
|
269
|
+
* @param events Events to process (same as state.events, provided for convenience)
|
|
270
|
+
* @returns RuleResult or array of new facts
|
|
188
271
|
*/
|
|
189
272
|
type RuleFn<TContext = unknown> = (state: PraxisState & {
|
|
190
273
|
context: TContext;
|
|
191
|
-
|
|
274
|
+
events: PraxisEvent[];
|
|
275
|
+
}, events: PraxisEvent[]) => RuleResult | PraxisFact[];
|
|
192
276
|
/**
|
|
193
277
|
* A constraint function checks that an invariant holds.
|
|
194
278
|
* Constraints must be pure - no side effects.
|
|
@@ -550,4 +634,4 @@ declare class ReactiveLogicEngine<TContext extends object> {
|
|
|
550
634
|
*/
|
|
551
635
|
declare function createReactiveEngine<TContext extends object>(options: ReactiveEngineOptions<TContext>): ReactiveLogicEngine<TContext>;
|
|
552
636
|
|
|
553
|
-
export { type Assumption as A, type ConstraintDescriptor as C, type DefineContractOptions as D, type Example as E, LogicEngine as L, type MissingArtifact as M, PraxisRegistry as P, type ReactiveEngineOptions as R, type Severity as S, type ValidationReport as V, ReactiveLogicEngine as a, type RuleDescriptor as b, createReactiveEngine as c, type ConstraintFn as d, type Contract as e, type RuleFn as f, type PraxisModule as g, type ConstraintId as h, type ContractGap as i, type PraxisEngineOptions as j, type Reference as k, type RuleId as l,
|
|
637
|
+
export { type Assumption as A, type ConstraintDescriptor as C, type DefineContractOptions as D, type Example as E, LogicEngine as L, type MissingArtifact as M, PraxisRegistry as P, type ReactiveEngineOptions as R, type Severity as S, type TypedRuleFn as T, type ValidationReport as V, ReactiveLogicEngine as a, type RuleDescriptor as b, createReactiveEngine as c, type ConstraintFn as d, type Contract as e, type RuleFn as f, type PraxisModule as g, type ConstraintId as h, type ContractGap as i, type PraxisEngineOptions as j, type Reference as k, type RuleId as l, RuleResult as m, createPraxisEngine as n, defineContract as o, fact as p, getContract as q, isContract as r };
|
package/dist/node/{reactive-engine.svelte-CGe8SpVE.d.cts → reactive-engine.svelte-DekxqFu0.d.ts}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as PraxisFact, P as PraxisState, a as PraxisEvent, c as PraxisStepResult, d as PraxisStepConfig, e as PraxisDiagnostics } from './protocol-DcyGMmWY.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Decision Ledger - Contract Types
|
|
@@ -162,6 +162,83 @@ interface ValidationReport {
|
|
|
162
162
|
timestamp: string;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/**
|
|
166
|
+
* The result of evaluating a rule. Every rule MUST return one of:
|
|
167
|
+
* - `RuleResult.emit(facts)` — rule produced facts
|
|
168
|
+
* - `RuleResult.noop(reason?)` — rule evaluated but had nothing to say
|
|
169
|
+
* - `RuleResult.skip(reason?)` — rule decided to skip (preconditions not met)
|
|
170
|
+
* - `RuleResult.retract(tags)` — rule retracts previously emitted facts
|
|
171
|
+
*/
|
|
172
|
+
declare class RuleResult {
|
|
173
|
+
/** The kind of result */
|
|
174
|
+
readonly kind: 'emit' | 'noop' | 'skip' | 'retract';
|
|
175
|
+
/** Facts produced (only for 'emit') */
|
|
176
|
+
readonly facts: PraxisFact[];
|
|
177
|
+
/** Fact tags to retract (only for 'retract') */
|
|
178
|
+
readonly retractTags: string[];
|
|
179
|
+
/** Optional reason (for noop/skip/retract — useful for debugging) */
|
|
180
|
+
readonly reason?: string;
|
|
181
|
+
/** The rule ID that produced this result (set by engine) */
|
|
182
|
+
ruleId?: string;
|
|
183
|
+
private constructor();
|
|
184
|
+
/**
|
|
185
|
+
* Rule produced facts.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* return RuleResult.emit([
|
|
189
|
+
* { tag: 'sprint.behind', payload: { deficit: 5 } }
|
|
190
|
+
* ]);
|
|
191
|
+
*/
|
|
192
|
+
static emit(facts: PraxisFact[]): RuleResult;
|
|
193
|
+
/**
|
|
194
|
+
* Rule evaluated but had nothing to report.
|
|
195
|
+
* Unlike returning [], this is explicit and traceable.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* if (ctx.completedHours >= expectedHours) {
|
|
199
|
+
* return RuleResult.noop('Sprint is on pace');
|
|
200
|
+
* }
|
|
201
|
+
*/
|
|
202
|
+
static noop(reason?: string): RuleResult;
|
|
203
|
+
/**
|
|
204
|
+
* Rule decided to skip because preconditions were not met.
|
|
205
|
+
* Distinct from noop: skip means "I can't evaluate", noop means "I evaluated and found nothing".
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* if (!ctx.sprintName) {
|
|
209
|
+
* return RuleResult.skip('No active sprint');
|
|
210
|
+
* }
|
|
211
|
+
*/
|
|
212
|
+
static skip(reason?: string): RuleResult;
|
|
213
|
+
/**
|
|
214
|
+
* Rule retracts previously emitted facts by tag.
|
|
215
|
+
* Used when a condition that previously produced facts is no longer true.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* // Sprint was behind, but caught up
|
|
219
|
+
* if (ctx.completedHours >= expectedHours) {
|
|
220
|
+
* return RuleResult.retract(['sprint.behind'], 'Sprint caught up');
|
|
221
|
+
* }
|
|
222
|
+
*/
|
|
223
|
+
static retract(tags: string[], reason?: string): RuleResult;
|
|
224
|
+
/** Whether this result produced facts */
|
|
225
|
+
get hasFacts(): boolean;
|
|
226
|
+
/** Whether this result retracts facts */
|
|
227
|
+
get hasRetractions(): boolean;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* A rule function that returns a typed RuleResult.
|
|
231
|
+
* New API — replaces the old PraxisFact[] return type.
|
|
232
|
+
*/
|
|
233
|
+
type TypedRuleFn<TContext = unknown> = (state: PraxisState & {
|
|
234
|
+
context: TContext;
|
|
235
|
+
events: PraxisEvent[];
|
|
236
|
+
}, events: PraxisEvent[]) => RuleResult;
|
|
237
|
+
/**
|
|
238
|
+
* Convenience: create a fact object (just a shorthand)
|
|
239
|
+
*/
|
|
240
|
+
declare function fact(tag: string, payload: unknown): PraxisFact;
|
|
241
|
+
|
|
165
242
|
/**
|
|
166
243
|
* Rules and Constraints System
|
|
167
244
|
*
|
|
@@ -182,13 +259,20 @@ type ConstraintId = string;
|
|
|
182
259
|
* A rule function derives new facts or transitions from context + input facts/events.
|
|
183
260
|
* Rules must be pure - no side effects.
|
|
184
261
|
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
262
|
+
* Returns either:
|
|
263
|
+
* - `RuleResult` (new API — typed, traceable, supports retraction)
|
|
264
|
+
* - `PraxisFact[]` (legacy — backward compatible, will be deprecated)
|
|
265
|
+
*
|
|
266
|
+
* The state parameter includes `events` — the current batch being processed.
|
|
267
|
+
*
|
|
268
|
+
* @param state Current Praxis state (includes state.events for current batch)
|
|
269
|
+
* @param events Events to process (same as state.events, provided for convenience)
|
|
270
|
+
* @returns RuleResult or array of new facts
|
|
188
271
|
*/
|
|
189
272
|
type RuleFn<TContext = unknown> = (state: PraxisState & {
|
|
190
273
|
context: TContext;
|
|
191
|
-
|
|
274
|
+
events: PraxisEvent[];
|
|
275
|
+
}, events: PraxisEvent[]) => RuleResult | PraxisFact[];
|
|
192
276
|
/**
|
|
193
277
|
* A constraint function checks that an invariant holds.
|
|
194
278
|
* Constraints must be pure - no side effects.
|
|
@@ -550,4 +634,4 @@ declare class ReactiveLogicEngine<TContext extends object> {
|
|
|
550
634
|
*/
|
|
551
635
|
declare function createReactiveEngine<TContext extends object>(options: ReactiveEngineOptions<TContext>): ReactiveLogicEngine<TContext>;
|
|
552
636
|
|
|
553
|
-
export { type Assumption as A, type ConstraintDescriptor as C, type DefineContractOptions as D, type Example as E, LogicEngine as L, type MissingArtifact as M, PraxisRegistry as P, type ReactiveEngineOptions as R, type Severity as S, type ValidationReport as V, ReactiveLogicEngine as a, type RuleDescriptor as b, createReactiveEngine as c, type ConstraintFn as d, type Contract as e, type RuleFn as f, type PraxisModule as g, type ConstraintId as h, type ContractGap as i, type PraxisEngineOptions as j, type Reference as k, type RuleId as l,
|
|
637
|
+
export { type Assumption as A, type ConstraintDescriptor as C, type DefineContractOptions as D, type Example as E, LogicEngine as L, type MissingArtifact as M, PraxisRegistry as P, type ReactiveEngineOptions as R, type Severity as S, type TypedRuleFn as T, type ValidationReport as V, ReactiveLogicEngine as a, type RuleDescriptor as b, createReactiveEngine as c, type ConstraintFn as d, type Contract as e, type RuleFn as f, type PraxisModule as g, type ConstraintId as h, type ContractGap as i, type PraxisEngineOptions as j, type Reference as k, type RuleId as l, RuleResult as m, createPraxisEngine as n, defineContract as o, fact as p, getContract as q, isContract as r };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plures/praxis",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "The Full Plures Application Framework - declarative schemas, logic engine, component generation, and local-first data",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@9.15.1",
|
|
@@ -138,4 +138,4 @@
|
|
|
138
138
|
"publishConfig": {
|
|
139
139
|
"access": "public"
|
|
140
140
|
}
|
|
141
|
-
}
|
|
141
|
+
}
|