@plures/praxis 1.4.4 → 2.0.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-6MVRT7CK.js +363 -0
- package/dist/browser/factory/index.d.ts +2 -1
- package/dist/browser/index.d.ts +7 -4
- package/dist/browser/index.js +18 -6
- package/dist/browser/integrations/svelte.d.ts +4 -3
- package/dist/browser/project/index.d.ts +2 -1
- package/dist/browser/{reactive-engine.svelte-DgVTqHLc.d.ts → reactive-engine.svelte-BwWadvAW.d.ts} +2 -1
- package/dist/browser/rule-result-DcXWe9tn.d.ts +206 -0
- package/dist/browser/{rules-i1LHpnGd.d.ts → rules-BaWMqxuG.d.ts} +2 -205
- package/dist/browser/unified/index.d.ts +239 -0
- package/dist/browser/unified/index.js +20 -0
- package/dist/node/chunk-6MVRT7CK.js +363 -0
- package/dist/node/cli/index.js +1 -1
- package/dist/node/index.cjs +367 -0
- package/dist/node/index.d.cts +4 -2
- package/dist/node/index.d.ts +4 -2
- package/dist/node/index.js +19 -7
- package/dist/node/integrations/svelte.d.cts +3 -2
- package/dist/node/integrations/svelte.d.ts +3 -2
- package/dist/node/integrations/svelte.js +2 -2
- package/dist/node/{reactive-engine.svelte-DekxqFu0.d.ts → reactive-engine.svelte-BBZLMzus.d.ts} +3 -79
- package/dist/node/{reactive-engine.svelte-Cg0Yc2Hs.d.cts → reactive-engine.svelte-Cbq_V20o.d.cts} +3 -79
- package/dist/node/rule-result-B9GMivAn.d.cts +80 -0
- package/dist/node/rule-result-Bo3sFMmN.d.ts +80 -0
- package/dist/node/unified/index.cjs +484 -0
- package/dist/node/unified/index.d.cts +240 -0
- package/dist/node/unified/index.d.ts +240 -0
- package/dist/node/unified/index.js +21 -0
- package/package.json +7 -1
- package/src/index.browser.ts +20 -0
- package/src/index.ts +21 -0
- package/src/unified/__tests__/unified.test.ts +396 -0
- package/src/unified/core.ts +517 -0
- package/src/unified/index.ts +32 -0
- package/src/unified/rules.ts +66 -0
- package/src/unified/types.ts +148 -0
- package/dist/node/{chunk-ZO2LU4G4.js → chunk-WFRHXZBP.js} +3 -3
- package/dist/node/{validate-5PSWJTIC.js → validate-BY7JNY7H.js} +1 -1
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
// src/unified/core.ts
|
|
2
|
+
var _idCounter = 0;
|
|
3
|
+
function nextId() {
|
|
4
|
+
return `px:${Date.now()}-${++_idCounter}`;
|
|
5
|
+
}
|
|
6
|
+
function createApp(config) {
|
|
7
|
+
const paths = /* @__PURE__ */ new Map();
|
|
8
|
+
for (const schema of config.schema) {
|
|
9
|
+
paths.set(schema.path, {
|
|
10
|
+
schema,
|
|
11
|
+
value: schema.initial,
|
|
12
|
+
subscribers: /* @__PURE__ */ new Set(),
|
|
13
|
+
lastUpdated: 0,
|
|
14
|
+
updateCount: 0
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
let facts = [];
|
|
18
|
+
const factMap = /* @__PURE__ */ new Map();
|
|
19
|
+
const timeline = [];
|
|
20
|
+
const maxTimeline = 1e4;
|
|
21
|
+
function recordTimeline(path, kind, data) {
|
|
22
|
+
const entry = {
|
|
23
|
+
id: nextId(),
|
|
24
|
+
timestamp: Date.now(),
|
|
25
|
+
path,
|
|
26
|
+
kind,
|
|
27
|
+
data
|
|
28
|
+
};
|
|
29
|
+
timeline.push(entry);
|
|
30
|
+
if (timeline.length > maxTimeline) {
|
|
31
|
+
timeline.splice(0, timeline.length - maxTimeline);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const ruleStates = (config.rules ?? []).map((rule) => ({
|
|
35
|
+
rule,
|
|
36
|
+
lastResult: null
|
|
37
|
+
}));
|
|
38
|
+
const constraints = config.constraints ?? [];
|
|
39
|
+
const livenessConfig = config.liveness;
|
|
40
|
+
const initTime = Date.now();
|
|
41
|
+
let livenessTimer = null;
|
|
42
|
+
if (livenessConfig) {
|
|
43
|
+
const timeout = livenessConfig.timeoutMs ?? 5e3;
|
|
44
|
+
livenessTimer = setTimeout(() => {
|
|
45
|
+
for (const expectedPath of livenessConfig.expect) {
|
|
46
|
+
const state = paths.get(expectedPath);
|
|
47
|
+
if (!state || state.updateCount === 0) {
|
|
48
|
+
const elapsed = Date.now() - initTime;
|
|
49
|
+
recordTimeline(expectedPath, "liveness", {
|
|
50
|
+
stale: true,
|
|
51
|
+
elapsed,
|
|
52
|
+
message: `Path "${expectedPath}" never updated after ${elapsed}ms`
|
|
53
|
+
});
|
|
54
|
+
livenessConfig.onStale?.(expectedPath, elapsed);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}, timeout);
|
|
58
|
+
}
|
|
59
|
+
function getPathValues(watchPaths) {
|
|
60
|
+
const values = {};
|
|
61
|
+
for (const p of watchPaths) {
|
|
62
|
+
const state = paths.get(p);
|
|
63
|
+
values[p] = state ? state.value : void 0;
|
|
64
|
+
}
|
|
65
|
+
return values;
|
|
66
|
+
}
|
|
67
|
+
function notify(path, value) {
|
|
68
|
+
const state = paths.get(path);
|
|
69
|
+
if (!state) return;
|
|
70
|
+
for (const cb of state.subscribers) {
|
|
71
|
+
try {
|
|
72
|
+
cb(value);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.error(`[praxis] Subscriber error for "${path}":`, err);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function checkConstraints(path, value) {
|
|
79
|
+
const violations = [];
|
|
80
|
+
for (const c of constraints) {
|
|
81
|
+
if (!c.watch.includes(path)) continue;
|
|
82
|
+
const values = getPathValues(c.watch);
|
|
83
|
+
values[path] = value;
|
|
84
|
+
try {
|
|
85
|
+
const result = c.validate(values);
|
|
86
|
+
if (result !== true) {
|
|
87
|
+
violations.push({
|
|
88
|
+
kind: "constraint-violation",
|
|
89
|
+
message: result,
|
|
90
|
+
data: { constraintId: c.id, path, description: c.description }
|
|
91
|
+
});
|
|
92
|
+
recordTimeline(path, "constraint-check", {
|
|
93
|
+
constraintId: c.id,
|
|
94
|
+
violated: true,
|
|
95
|
+
message: result
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
} catch (err) {
|
|
99
|
+
violations.push({
|
|
100
|
+
kind: "constraint-violation",
|
|
101
|
+
message: `Constraint "${c.id}" threw: ${err instanceof Error ? err.message : String(err)}`,
|
|
102
|
+
data: { constraintId: c.id, error: err }
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return violations;
|
|
107
|
+
}
|
|
108
|
+
function evaluateRules() {
|
|
109
|
+
const newFacts = [];
|
|
110
|
+
const retractions = [];
|
|
111
|
+
for (const rs of ruleStates) {
|
|
112
|
+
const values = getPathValues(rs.rule.watch);
|
|
113
|
+
try {
|
|
114
|
+
const result = rs.rule.evaluate(values, [...facts]);
|
|
115
|
+
rs.lastResult = result;
|
|
116
|
+
recordTimeline(rs.rule.watch[0] ?? "*", "rule-eval", {
|
|
117
|
+
ruleId: rs.rule.id,
|
|
118
|
+
kind: result.kind,
|
|
119
|
+
reason: result.reason
|
|
120
|
+
});
|
|
121
|
+
switch (result.kind) {
|
|
122
|
+
case "emit":
|
|
123
|
+
newFacts.push(...result.facts);
|
|
124
|
+
break;
|
|
125
|
+
case "retract":
|
|
126
|
+
retractions.push(...result.retractTags);
|
|
127
|
+
break;
|
|
128
|
+
case "noop":
|
|
129
|
+
case "skip":
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.error(`[praxis] Rule "${rs.rule.id}" error:`, err);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (retractions.length > 0) {
|
|
137
|
+
const retractSet = new Set(retractions);
|
|
138
|
+
for (const tag of retractSet) {
|
|
139
|
+
factMap.delete(tag);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
for (const f of newFacts) {
|
|
143
|
+
factMap.set(f.tag, f);
|
|
144
|
+
}
|
|
145
|
+
facts = Array.from(factMap.values());
|
|
146
|
+
}
|
|
147
|
+
function query(path, opts) {
|
|
148
|
+
let state = paths.get(path);
|
|
149
|
+
if (!state) {
|
|
150
|
+
state = {
|
|
151
|
+
schema: { path, initial: void 0 },
|
|
152
|
+
value: void 0,
|
|
153
|
+
subscribers: /* @__PURE__ */ new Set(),
|
|
154
|
+
lastUpdated: 0,
|
|
155
|
+
updateCount: 0
|
|
156
|
+
};
|
|
157
|
+
paths.set(path, state);
|
|
158
|
+
}
|
|
159
|
+
const ref = {
|
|
160
|
+
get current() {
|
|
161
|
+
const s = paths.get(path);
|
|
162
|
+
return applyQueryOpts(s?.value ?? state.schema.initial, opts);
|
|
163
|
+
},
|
|
164
|
+
subscribe(cb) {
|
|
165
|
+
const wrappedCb = (rawValue) => {
|
|
166
|
+
const processed = applyQueryOpts(rawValue, opts);
|
|
167
|
+
cb(processed);
|
|
168
|
+
};
|
|
169
|
+
const s = paths.get(path);
|
|
170
|
+
s.subscribers.add(wrappedCb);
|
|
171
|
+
try {
|
|
172
|
+
cb(ref.current);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error(`[praxis] query("${path}") subscriber init error:`, err);
|
|
175
|
+
}
|
|
176
|
+
return () => {
|
|
177
|
+
s.subscribers.delete(wrappedCb);
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
return ref;
|
|
182
|
+
}
|
|
183
|
+
function applyQueryOpts(value, opts) {
|
|
184
|
+
if (!opts || !Array.isArray(value)) return value;
|
|
185
|
+
let result = [...value];
|
|
186
|
+
if (opts.where) result = result.filter(opts.where);
|
|
187
|
+
if (opts.sort) result.sort(opts.sort);
|
|
188
|
+
if (opts.select) result = result.map(opts.select);
|
|
189
|
+
if (opts.limit) result = result.slice(0, opts.limit);
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
function mutateInternal(path, value) {
|
|
193
|
+
const violations = checkConstraints(path, value);
|
|
194
|
+
if (violations.length > 0) {
|
|
195
|
+
return { violations, emittedFacts: [] };
|
|
196
|
+
}
|
|
197
|
+
const state = paths.get(path);
|
|
198
|
+
if (!state) {
|
|
199
|
+
paths.set(path, {
|
|
200
|
+
schema: { path, initial: value },
|
|
201
|
+
value,
|
|
202
|
+
subscribers: /* @__PURE__ */ new Set(),
|
|
203
|
+
lastUpdated: Date.now(),
|
|
204
|
+
updateCount: 1
|
|
205
|
+
});
|
|
206
|
+
} else {
|
|
207
|
+
const before = state.value;
|
|
208
|
+
state.value = value;
|
|
209
|
+
state.lastUpdated = Date.now();
|
|
210
|
+
state.updateCount++;
|
|
211
|
+
recordTimeline(path, "mutation", {
|
|
212
|
+
before: summarize(before),
|
|
213
|
+
after: summarize(value)
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
notify(path, value);
|
|
217
|
+
const factsBefore = facts.length;
|
|
218
|
+
evaluateRules();
|
|
219
|
+
const emittedFacts = facts.slice(factsBefore);
|
|
220
|
+
return { violations: [], emittedFacts };
|
|
221
|
+
}
|
|
222
|
+
function mutate(path, value) {
|
|
223
|
+
const { violations, emittedFacts } = mutateInternal(path, value);
|
|
224
|
+
return {
|
|
225
|
+
accepted: violations.length === 0,
|
|
226
|
+
violations,
|
|
227
|
+
facts: emittedFacts
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function batchMutate(fn) {
|
|
231
|
+
const allViolations = [];
|
|
232
|
+
const pendingWrites = [];
|
|
233
|
+
fn((path, value) => {
|
|
234
|
+
const violations = checkConstraints(path, value);
|
|
235
|
+
if (violations.length > 0) {
|
|
236
|
+
allViolations.push(...violations);
|
|
237
|
+
} else {
|
|
238
|
+
pendingWrites.push({ path, value });
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
if (allViolations.length > 0) {
|
|
242
|
+
return { accepted: false, violations: allViolations, facts: [] };
|
|
243
|
+
}
|
|
244
|
+
for (const { path, value } of pendingWrites) {
|
|
245
|
+
const state = paths.get(path);
|
|
246
|
+
if (state) {
|
|
247
|
+
const before = state.value;
|
|
248
|
+
state.value = value;
|
|
249
|
+
state.lastUpdated = Date.now();
|
|
250
|
+
state.updateCount++;
|
|
251
|
+
recordTimeline(path, "mutation", {
|
|
252
|
+
before: summarize(before),
|
|
253
|
+
after: summarize(value)
|
|
254
|
+
});
|
|
255
|
+
} else {
|
|
256
|
+
paths.set(path, {
|
|
257
|
+
schema: { path, initial: value },
|
|
258
|
+
value,
|
|
259
|
+
subscribers: /* @__PURE__ */ new Set(),
|
|
260
|
+
lastUpdated: Date.now(),
|
|
261
|
+
updateCount: 1
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
for (const { path, value } of pendingWrites) {
|
|
266
|
+
notify(path, value);
|
|
267
|
+
}
|
|
268
|
+
const factsBefore = facts.length;
|
|
269
|
+
evaluateRules();
|
|
270
|
+
return {
|
|
271
|
+
accepted: true,
|
|
272
|
+
violations: [],
|
|
273
|
+
facts: facts.slice(factsBefore)
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function getLiveness() {
|
|
277
|
+
const result = {};
|
|
278
|
+
const now = Date.now();
|
|
279
|
+
const watchPaths = livenessConfig?.expect ?? [];
|
|
280
|
+
for (const p of watchPaths) {
|
|
281
|
+
const state = paths.get(p);
|
|
282
|
+
const lastUpdated = state?.lastUpdated ?? 0;
|
|
283
|
+
const elapsed = lastUpdated > 0 ? now - lastUpdated : now - initTime;
|
|
284
|
+
const timeout = livenessConfig?.timeoutMs ?? 5e3;
|
|
285
|
+
result[p] = {
|
|
286
|
+
stale: state?.updateCount === 0 || elapsed > timeout,
|
|
287
|
+
lastUpdated,
|
|
288
|
+
elapsed
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
function destroy() {
|
|
294
|
+
if (livenessTimer) clearTimeout(livenessTimer);
|
|
295
|
+
for (const state of paths.values()) {
|
|
296
|
+
state.subscribers.clear();
|
|
297
|
+
}
|
|
298
|
+
paths.clear();
|
|
299
|
+
facts = [];
|
|
300
|
+
factMap.clear();
|
|
301
|
+
timeline.length = 0;
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
query,
|
|
305
|
+
mutate,
|
|
306
|
+
batch: batchMutate,
|
|
307
|
+
facts: () => [...facts],
|
|
308
|
+
violations: () => {
|
|
309
|
+
const allViolations = [];
|
|
310
|
+
for (const c of constraints) {
|
|
311
|
+
const values = getPathValues(c.watch);
|
|
312
|
+
try {
|
|
313
|
+
const result = c.validate(values);
|
|
314
|
+
if (result !== true) {
|
|
315
|
+
allViolations.push({
|
|
316
|
+
kind: "constraint-violation",
|
|
317
|
+
message: result,
|
|
318
|
+
data: { constraintId: c.id }
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
} catch {
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return allViolations;
|
|
325
|
+
},
|
|
326
|
+
timeline: () => [...timeline],
|
|
327
|
+
evaluate: evaluateRules,
|
|
328
|
+
destroy,
|
|
329
|
+
liveness: getLiveness
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function summarize(value) {
|
|
333
|
+
if (value === null || value === void 0) return value;
|
|
334
|
+
if (typeof value !== "object") return value;
|
|
335
|
+
if (Array.isArray(value)) return `[Array(${value.length})]`;
|
|
336
|
+
const keys = Object.keys(value);
|
|
337
|
+
if (keys.length > 10) return `{Object(${keys.length} keys)}`;
|
|
338
|
+
return value;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/unified/types.ts
|
|
342
|
+
function definePath(path, initial, opts) {
|
|
343
|
+
return { path, initial, ...opts };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/unified/rules.ts
|
|
347
|
+
function defineRule(rule) {
|
|
348
|
+
return rule;
|
|
349
|
+
}
|
|
350
|
+
function defineConstraint(constraint) {
|
|
351
|
+
return constraint;
|
|
352
|
+
}
|
|
353
|
+
function defineModule(name, rules) {
|
|
354
|
+
return { name, rules };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export {
|
|
358
|
+
createApp,
|
|
359
|
+
definePath,
|
|
360
|
+
defineRule,
|
|
361
|
+
defineConstraint,
|
|
362
|
+
defineModule
|
|
363
|
+
};
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { P as PraxisState, a as PraxisEvent, b as
|
|
2
|
-
export {
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
1
|
+
import { P as PraxisState, a as PraxisEvent, b as PraxisFact } from './rule-result-DcXWe9tn.js';
|
|
2
|
+
export { f as PRAXIS_PROTOCOL_VERSION, e as PraxisDiagnostics, d as PraxisStepConfig, g as PraxisStepFn, c as PraxisStepResult, R as RuleResult, T as TypedRuleFn, h as fact } from './rule-result-DcXWe9tn.js';
|
|
3
|
+
import { P as PraxisRegistry, R as RuleDescriptor, C as ConstraintDescriptor, a as ConstraintFn, b as Contract, c as RuleFn, d as PraxisModule } from './rules-BaWMqxuG.js';
|
|
4
|
+
export { e as ConstraintId, f as RuleId } from './rules-BaWMqxuG.js';
|
|
5
|
+
import { L as LogicEngine } from './reactive-engine.svelte-BwWadvAW.js';
|
|
6
|
+
export { P as PraxisEngineOptions, R as ReactiveEngineOptions, a as ReactiveLogicEngine, b as createPraxisEngine, c as createReactiveEngine } from './reactive-engine.svelte-BwWadvAW.js';
|
|
5
7
|
import { LocalFirstOptions } from '@plures/pluresdb/local-first';
|
|
6
8
|
export { ConditionResult, ConditionStatus, Expectation, ExpectationCondition, ExpectationResult, ExpectationSet, ExpectationSetOptions, VerifiableDescriptor, VerifiableRegistry, VerificationReport, expectBehavior, formatVerificationReport, verify } from './expectations/index.js';
|
|
7
9
|
export { DataRulesConfig, FormRulesConfig, InputRulesConfig, NavigationRulesConfig, SanitizationType, ToastRulesConfig, dataRules, formRules, inputRules, navigationRules, toastRules } from './factory/index.js';
|
|
8
10
|
export { BranchRulesConfig, GateConfig, GateState, GateStatus, PraxisDiff, PredefinedGateConfig, SemverContractConfig, SemverReport, branchRules, commitFromState, defineGate, expectationGate, formatGate, lintGate, semverContract } from './project/index.js';
|
|
11
|
+
export { LivenessConfig, MutationResult, PathSchema, PraxisApp, PraxisAppConfig, QueryOptions, ReactiveRef, UnifiedConstraint, UnifiedRule, createApp, definePath, defineConstraint as defineUnifiedConstraint, defineModule as defineUnifiedModule, defineRule as defineUnifiedRule } from './unified/index.js';
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* Praxis Reactive Logic Engine
|
package/dist/browser/index.js
CHANGED
|
@@ -26,6 +26,13 @@ import {
|
|
|
26
26
|
lintGate,
|
|
27
27
|
semverContract
|
|
28
28
|
} from "./chunk-BQOYZBWA.js";
|
|
29
|
+
import {
|
|
30
|
+
createApp,
|
|
31
|
+
defineConstraint,
|
|
32
|
+
defineModule,
|
|
33
|
+
definePath,
|
|
34
|
+
defineRule
|
|
35
|
+
} from "./chunk-6MVRT7CK.js";
|
|
29
36
|
import {
|
|
30
37
|
LogicEngine,
|
|
31
38
|
PRAXIS_PROTOCOL_VERSION,
|
|
@@ -606,7 +613,7 @@ function defineEvent(tag) {
|
|
|
606
613
|
}
|
|
607
614
|
};
|
|
608
615
|
}
|
|
609
|
-
function
|
|
616
|
+
function defineRule2(options) {
|
|
610
617
|
const contract = options.contract ?? options.meta?.contract;
|
|
611
618
|
const meta = contract ? { ...options.meta ?? {}, contract } : options.meta;
|
|
612
619
|
return {
|
|
@@ -618,7 +625,7 @@ function defineRule(options) {
|
|
|
618
625
|
meta
|
|
619
626
|
};
|
|
620
627
|
}
|
|
621
|
-
function
|
|
628
|
+
function defineConstraint2(options) {
|
|
622
629
|
const contract = options.contract ?? options.meta?.contract;
|
|
623
630
|
const meta = contract ? { ...options.meta ?? {}, contract } : options.meta;
|
|
624
631
|
return {
|
|
@@ -629,7 +636,7 @@ function defineConstraint(options) {
|
|
|
629
636
|
meta
|
|
630
637
|
};
|
|
631
638
|
}
|
|
632
|
-
function
|
|
639
|
+
function defineModule2(options) {
|
|
633
640
|
return {
|
|
634
641
|
rules: options.rules ?? [],
|
|
635
642
|
constraints: options.constraints ?? [],
|
|
@@ -3370,6 +3377,7 @@ export {
|
|
|
3370
3377
|
canvasToSchema,
|
|
3371
3378
|
canvasToYaml,
|
|
3372
3379
|
commitFromState,
|
|
3380
|
+
createApp,
|
|
3373
3381
|
createCanvasEditor,
|
|
3374
3382
|
createReactiveEngine2 as createFrameworkAgnosticReactiveEngine,
|
|
3375
3383
|
createInMemoryDB,
|
|
@@ -3392,12 +3400,16 @@ export {
|
|
|
3392
3400
|
createUnifiedApp,
|
|
3393
3401
|
createUnumAdapter,
|
|
3394
3402
|
dataRules,
|
|
3395
|
-
defineConstraint,
|
|
3403
|
+
defineConstraint2 as defineConstraint,
|
|
3396
3404
|
defineEvent,
|
|
3397
3405
|
defineFact,
|
|
3398
3406
|
defineGate,
|
|
3399
|
-
defineModule,
|
|
3400
|
-
|
|
3407
|
+
defineModule2 as defineModule,
|
|
3408
|
+
definePath,
|
|
3409
|
+
defineRule2 as defineRule,
|
|
3410
|
+
defineConstraint as defineUnifiedConstraint,
|
|
3411
|
+
defineModule as defineUnifiedModule,
|
|
3412
|
+
defineRule as defineUnifiedRule,
|
|
3401
3413
|
dirtyGuardRule,
|
|
3402
3414
|
errorDisplayRule,
|
|
3403
3415
|
expectBehavior,
|
|
@@ -1,6 +1,7 @@
|
|
|
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 '../
|
|
1
|
+
import { L as LogicEngine } from '../reactive-engine.svelte-BwWadvAW.js';
|
|
2
|
+
export { R as ReactiveEngineOptions, a as ReactiveLogicEngine, c as createReactiveEngine } from '../reactive-engine.svelte-BwWadvAW.js';
|
|
3
|
+
import { P as PraxisState, a as PraxisEvent } from '../rule-result-DcXWe9tn.js';
|
|
4
|
+
import '../rules-BaWMqxuG.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Svelte v5 Integration
|
package/dist/browser/{reactive-engine.svelte-DgVTqHLc.d.ts → reactive-engine.svelte-BwWadvAW.d.ts}
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { b as
|
|
1
|
+
import { b as PraxisFact, P as PraxisState, a as PraxisEvent, c as PraxisStepResult, d as PraxisStepConfig, e as PraxisDiagnostics } from './rule-result-DcXWe9tn.js';
|
|
2
|
+
import { P as PraxisRegistry } from './rules-BaWMqxuG.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Praxis Logic Engine
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Praxis Protocol
|
|
3
|
+
*
|
|
4
|
+
* Language-neutral, JSON-friendly protocol that forms the foundation of Praxis.
|
|
5
|
+
* This protocol is designed to be stable and portable across languages (TypeScript, C#, PowerShell, etc.)
|
|
6
|
+
*
|
|
7
|
+
* The protocol defines the conceptual core of the engine:
|
|
8
|
+
* - Pure, deterministic, data in → data out
|
|
9
|
+
* - No side effects, no global state
|
|
10
|
+
* - All higher-level TypeScript APIs are built on top of this protocol
|
|
11
|
+
*
|
|
12
|
+
* ## Protocol Versioning
|
|
13
|
+
*
|
|
14
|
+
* The Praxis protocol follows semantic versioning (MAJOR.MINOR.PATCH):
|
|
15
|
+
* - MAJOR: Breaking changes to core protocol types or semantics
|
|
16
|
+
* - MINOR: Backward-compatible additions to protocol (new optional fields)
|
|
17
|
+
* - PATCH: Clarifications, documentation updates, no functional changes
|
|
18
|
+
*
|
|
19
|
+
* Current version: 1.0.0
|
|
20
|
+
*
|
|
21
|
+
* ### Stability Guarantees
|
|
22
|
+
*
|
|
23
|
+
* 1. **Core Types Stability**: The following types are considered stable and will not
|
|
24
|
+
* change in backward-incompatible ways within the same major version:
|
|
25
|
+
* - PraxisFact (tag, payload structure)
|
|
26
|
+
* - PraxisEvent (tag, payload structure)
|
|
27
|
+
* - PraxisState (context, facts, meta structure)
|
|
28
|
+
* - PraxisStepFn signature
|
|
29
|
+
*
|
|
30
|
+
* 2. **JSON Compatibility**: All protocol types will remain JSON-serializable.
|
|
31
|
+
* No non-JSON-safe types (functions, symbols, etc.) will be added to the protocol.
|
|
32
|
+
*
|
|
33
|
+
* 3. **Cross-Language Compatibility**: Protocol changes will be coordinated across
|
|
34
|
+
* all official language implementations (TypeScript, C#, PowerShell) to ensure
|
|
35
|
+
* interoperability.
|
|
36
|
+
*
|
|
37
|
+
* 4. **Migration Path**: Major version changes will be accompanied by:
|
|
38
|
+
* - Migration guide
|
|
39
|
+
* - Deprecation warnings in previous version
|
|
40
|
+
* - Compatibility shims where possible
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Protocol version following semantic versioning
|
|
44
|
+
*/
|
|
45
|
+
declare const PRAXIS_PROTOCOL_VERSION: "1.0.0";
|
|
46
|
+
/**
|
|
47
|
+
* A fact is a typed proposition about the domain.
|
|
48
|
+
* Examples: UserLoggedIn, CartItem, NetworkOnline
|
|
49
|
+
*/
|
|
50
|
+
interface PraxisFact {
|
|
51
|
+
/** Tag identifying the fact type */
|
|
52
|
+
tag: string;
|
|
53
|
+
/** Payload containing the fact data */
|
|
54
|
+
payload: unknown;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* An event is a temporally ordered fact meant to drive change.
|
|
58
|
+
* Examples: LOGIN, LOGOUT, ADD_TO_CART
|
|
59
|
+
*/
|
|
60
|
+
interface PraxisEvent {
|
|
61
|
+
/** Tag identifying the event type */
|
|
62
|
+
tag: string;
|
|
63
|
+
/** Payload containing the event data */
|
|
64
|
+
payload: unknown;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The state of the Praxis engine at a point in time.
|
|
68
|
+
*/
|
|
69
|
+
interface PraxisState {
|
|
70
|
+
/** Application context (domain-specific data) */
|
|
71
|
+
context: unknown;
|
|
72
|
+
/** Current facts about the domain */
|
|
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[];
|
|
81
|
+
/** Optional metadata (timestamps, version, etc.) */
|
|
82
|
+
meta?: Record<string, unknown>;
|
|
83
|
+
/** Protocol version (for cross-language compatibility) */
|
|
84
|
+
protocolVersion?: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Diagnostic information about constraint violations or rule errors.
|
|
88
|
+
*/
|
|
89
|
+
interface PraxisDiagnostics {
|
|
90
|
+
/** Kind of diagnostic */
|
|
91
|
+
kind: 'constraint-violation' | 'rule-error';
|
|
92
|
+
/** Human-readable message */
|
|
93
|
+
message: string;
|
|
94
|
+
/** Additional diagnostic data */
|
|
95
|
+
data?: unknown;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Configuration for a step execution.
|
|
99
|
+
* Specifies which rules and constraints to apply.
|
|
100
|
+
*/
|
|
101
|
+
interface PraxisStepConfig {
|
|
102
|
+
/** IDs of rules to apply during this step */
|
|
103
|
+
ruleIds: string[];
|
|
104
|
+
/** IDs of constraints to check during this step */
|
|
105
|
+
constraintIds: string[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Result of a step execution.
|
|
109
|
+
*/
|
|
110
|
+
interface PraxisStepResult {
|
|
111
|
+
/** New state after applying rules and checking constraints */
|
|
112
|
+
state: PraxisState;
|
|
113
|
+
/** Diagnostics from rule execution and constraint checking */
|
|
114
|
+
diagnostics: PraxisDiagnostics[];
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The core step function of the Praxis engine.
|
|
118
|
+
*
|
|
119
|
+
* This is the conceptual heart of the engine:
|
|
120
|
+
* - Takes current state, events, and configuration
|
|
121
|
+
* - Applies rules and checks constraints
|
|
122
|
+
* - Returns new state and diagnostics
|
|
123
|
+
*
|
|
124
|
+
* Pure, deterministic, data in → data out.
|
|
125
|
+
* No side effects, no global state.
|
|
126
|
+
*/
|
|
127
|
+
type PraxisStepFn = (state: PraxisState, events: PraxisEvent[], config: PraxisStepConfig) => PraxisStepResult;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The result of evaluating a rule. Every rule MUST return one of:
|
|
131
|
+
* - `RuleResult.emit(facts)` — rule produced facts
|
|
132
|
+
* - `RuleResult.noop(reason?)` — rule evaluated but had nothing to say
|
|
133
|
+
* - `RuleResult.skip(reason?)` — rule decided to skip (preconditions not met)
|
|
134
|
+
* - `RuleResult.retract(tags)` — rule retracts previously emitted facts
|
|
135
|
+
*/
|
|
136
|
+
declare class RuleResult {
|
|
137
|
+
/** The kind of result */
|
|
138
|
+
readonly kind: 'emit' | 'noop' | 'skip' | 'retract';
|
|
139
|
+
/** Facts produced (only for 'emit') */
|
|
140
|
+
readonly facts: PraxisFact[];
|
|
141
|
+
/** Fact tags to retract (only for 'retract') */
|
|
142
|
+
readonly retractTags: string[];
|
|
143
|
+
/** Optional reason (for noop/skip/retract — useful for debugging) */
|
|
144
|
+
readonly reason?: string;
|
|
145
|
+
/** The rule ID that produced this result (set by engine) */
|
|
146
|
+
ruleId?: string;
|
|
147
|
+
private constructor();
|
|
148
|
+
/**
|
|
149
|
+
* Rule produced facts.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* return RuleResult.emit([
|
|
153
|
+
* { tag: 'sprint.behind', payload: { deficit: 5 } }
|
|
154
|
+
* ]);
|
|
155
|
+
*/
|
|
156
|
+
static emit(facts: PraxisFact[]): RuleResult;
|
|
157
|
+
/**
|
|
158
|
+
* Rule evaluated but had nothing to report.
|
|
159
|
+
* Unlike returning [], this is explicit and traceable.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* if (ctx.completedHours >= expectedHours) {
|
|
163
|
+
* return RuleResult.noop('Sprint is on pace');
|
|
164
|
+
* }
|
|
165
|
+
*/
|
|
166
|
+
static noop(reason?: string): RuleResult;
|
|
167
|
+
/**
|
|
168
|
+
* Rule decided to skip because preconditions were not met.
|
|
169
|
+
* Distinct from noop: skip means "I can't evaluate", noop means "I evaluated and found nothing".
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* if (!ctx.sprintName) {
|
|
173
|
+
* return RuleResult.skip('No active sprint');
|
|
174
|
+
* }
|
|
175
|
+
*/
|
|
176
|
+
static skip(reason?: string): RuleResult;
|
|
177
|
+
/**
|
|
178
|
+
* Rule retracts previously emitted facts by tag.
|
|
179
|
+
* Used when a condition that previously produced facts is no longer true.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* // Sprint was behind, but caught up
|
|
183
|
+
* if (ctx.completedHours >= expectedHours) {
|
|
184
|
+
* return RuleResult.retract(['sprint.behind'], 'Sprint caught up');
|
|
185
|
+
* }
|
|
186
|
+
*/
|
|
187
|
+
static retract(tags: string[], reason?: string): RuleResult;
|
|
188
|
+
/** Whether this result produced facts */
|
|
189
|
+
get hasFacts(): boolean;
|
|
190
|
+
/** Whether this result retracts facts */
|
|
191
|
+
get hasRetractions(): boolean;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* A rule function that returns a typed RuleResult.
|
|
195
|
+
* New API — replaces the old PraxisFact[] return type.
|
|
196
|
+
*/
|
|
197
|
+
type TypedRuleFn<TContext = unknown> = (state: PraxisState & {
|
|
198
|
+
context: TContext;
|
|
199
|
+
events: PraxisEvent[];
|
|
200
|
+
}, events: PraxisEvent[]) => RuleResult;
|
|
201
|
+
/**
|
|
202
|
+
* Convenience: create a fact object (just a shorthand)
|
|
203
|
+
*/
|
|
204
|
+
declare function fact(tag: string, payload: unknown): PraxisFact;
|
|
205
|
+
|
|
206
|
+
export { type PraxisState as P, RuleResult as R, type TypedRuleFn as T, type PraxisEvent as a, type PraxisFact as b, type PraxisStepResult as c, type PraxisStepConfig as d, type PraxisDiagnostics as e, PRAXIS_PROTOCOL_VERSION as f, type PraxisStepFn as g, fact as h };
|