@plures/praxis 1.1.3 → 1.2.10
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/FRAMEWORK.md +106 -15
- package/README.md +194 -119
- package/dist/browser/adapter-CIMBGDC7.js +14 -0
- package/dist/browser/chunk-K377RW4V.js +230 -0
- package/dist/browser/chunk-MBVHLOU2.js +152 -0
- package/dist/browser/{chunk-R45WXWKH.js → chunk-VOMLVI6V.js} +1 -149
- package/dist/browser/engine-YJZV4SLD.js +8 -0
- package/dist/browser/index.d.ts +161 -5
- package/dist/browser/index.js +156 -141
- package/dist/browser/integrations/svelte.d.ts +2 -2
- package/dist/browser/integrations/svelte.js +2 -1
- package/dist/browser/{reactive-engine.svelte-C9OpcTHf.d.ts → reactive-engine.svelte-9aS0kTa8.d.ts} +136 -1
- package/dist/node/adapter-75ISSMWD.js +15 -0
- package/dist/node/chunk-5RH7UAQC.js +486 -0
- package/dist/node/chunk-MBVHLOU2.js +152 -0
- package/dist/node/chunk-PRPQO6R5.js +85 -0
- package/dist/node/chunk-R2PSBPKQ.js +150 -0
- package/dist/node/chunk-S54337I5.js +446 -0
- package/dist/node/{chunk-R45WXWKH.js → chunk-VOMLVI6V.js} +1 -149
- package/dist/node/chunk-WZ6B3LZ6.js +638 -0
- package/dist/node/cli/index.cjs +2936 -897
- package/dist/node/cli/index.js +27 -0
- package/dist/node/components/index.d.cts +3 -2
- package/dist/node/components/index.d.ts +3 -2
- package/dist/node/docs-JFNYTOJA.js +102 -0
- package/dist/node/engine-2DQBKBJC.js +9 -0
- package/dist/node/index.cjs +1114 -354
- package/dist/node/index.d.cts +388 -5
- package/dist/node/index.d.ts +388 -5
- package/dist/node/index.js +201 -640
- package/dist/node/integrations/svelte.cjs +76 -0
- package/dist/node/integrations/svelte.d.cts +2 -2
- package/dist/node/integrations/svelte.d.ts +2 -2
- package/dist/node/integrations/svelte.js +3 -1
- package/dist/node/{reactive-engine.svelte-1M4m_C_v.d.cts → reactive-engine.svelte-BFIZfawz.d.cts} +199 -1
- package/dist/node/{reactive-engine.svelte-ChNFn4Hj.d.ts → reactive-engine.svelte-CRNqHlbv.d.ts} +199 -1
- package/dist/node/reverse-W7THPV45.js +193 -0
- package/dist/node/{terminal-adapter-CWka-yL8.d.ts → terminal-adapter-B-UK_Vdz.d.ts} +28 -3
- package/dist/node/{terminal-adapter-CDzxoLKR.d.cts → terminal-adapter-BQSIF5bf.d.cts} +28 -3
- package/dist/node/validate-CNHUULQE.js +180 -0
- package/docs/core/pluresdb-integration.md +15 -15
- package/docs/decision-ledger/BEHAVIOR_LEDGER.md +225 -0
- package/docs/decision-ledger/DecisionLedger.tla +180 -0
- package/docs/decision-ledger/IMPLEMENTATION_SUMMARY.md +217 -0
- package/docs/decision-ledger/LATEST.md +166 -0
- package/docs/guides/cicd-pipeline.md +142 -0
- package/package.json +2 -2
- package/src/__tests__/cli-validate.test.ts +197 -0
- package/src/__tests__/decision-ledger.test.ts +485 -0
- package/src/__tests__/reverse-generator.test.ts +189 -0
- package/src/__tests__/scanner.test.ts +215 -0
- package/src/cli/commands/docs.ts +147 -0
- package/src/cli/commands/reverse.ts +289 -0
- package/src/cli/commands/validate.ts +264 -0
- package/src/cli/index.ts +68 -0
- package/src/core/pluresdb/adapter.ts +46 -3
- package/src/core/reactive-engine.svelte.ts +6 -1
- package/src/core/reactive-engine.ts +1 -1
- package/src/core/rules.ts +133 -0
- package/src/decision-ledger/README.md +400 -0
- package/src/decision-ledger/REVERSE_ENGINEERING.md +484 -0
- package/src/decision-ledger/facts-events.ts +121 -0
- package/src/decision-ledger/index.ts +70 -0
- package/src/decision-ledger/ledger.ts +246 -0
- package/src/decision-ledger/logic-ledger.ts +158 -0
- package/src/decision-ledger/reverse-generator.ts +426 -0
- package/src/decision-ledger/scanner.ts +506 -0
- package/src/decision-ledger/types.ts +247 -0
- package/src/decision-ledger/validation.ts +336 -0
- package/src/dsl/index.ts +13 -2
- package/src/index.browser.ts +6 -0
- package/src/index.ts +40 -0
- package/src/integrations/pluresdb.ts +14 -2
- package/src/integrations/unified.ts +350 -0
package/dist/node/index.cjs
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,10 +30,386 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
33
|
+
// src/core/protocol.ts
|
|
34
|
+
var PRAXIS_PROTOCOL_VERSION;
|
|
35
|
+
var init_protocol = __esm({
|
|
36
|
+
"src/core/protocol.ts"() {
|
|
37
|
+
"use strict";
|
|
38
|
+
PRAXIS_PROTOCOL_VERSION = "1.0.0";
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// src/core/engine.ts
|
|
43
|
+
var engine_exports = {};
|
|
44
|
+
__export(engine_exports, {
|
|
45
|
+
LogicEngine: () => LogicEngine,
|
|
46
|
+
createPraxisEngine: () => createPraxisEngine
|
|
47
|
+
});
|
|
48
|
+
function safeClone(value) {
|
|
49
|
+
if (value === null || typeof value !== "object") {
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
if (typeof globalThis.structuredClone === "function") {
|
|
53
|
+
try {
|
|
54
|
+
return globalThis.structuredClone(value);
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
return [...value];
|
|
60
|
+
}
|
|
61
|
+
return { ...value };
|
|
62
|
+
}
|
|
63
|
+
function createPraxisEngine(options) {
|
|
64
|
+
return new LogicEngine(options);
|
|
65
|
+
}
|
|
66
|
+
var LogicEngine;
|
|
67
|
+
var init_engine = __esm({
|
|
68
|
+
"src/core/engine.ts"() {
|
|
69
|
+
"use strict";
|
|
70
|
+
init_protocol();
|
|
71
|
+
LogicEngine = class {
|
|
72
|
+
state;
|
|
73
|
+
registry;
|
|
74
|
+
constructor(options) {
|
|
75
|
+
this.registry = options.registry;
|
|
76
|
+
this.state = {
|
|
77
|
+
context: options.initialContext,
|
|
78
|
+
facts: options.initialFacts ?? [],
|
|
79
|
+
meta: options.initialMeta ?? {},
|
|
80
|
+
protocolVersion: PRAXIS_PROTOCOL_VERSION
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get the current state (immutable copy)
|
|
85
|
+
*/
|
|
86
|
+
getState() {
|
|
87
|
+
return {
|
|
88
|
+
context: safeClone(this.state.context),
|
|
89
|
+
facts: [...this.state.facts],
|
|
90
|
+
meta: this.state.meta ? safeClone(this.state.meta) : void 0,
|
|
91
|
+
protocolVersion: this.state.protocolVersion
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get the current context
|
|
96
|
+
*/
|
|
97
|
+
getContext() {
|
|
98
|
+
return safeClone(this.state.context);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get current facts
|
|
102
|
+
*/
|
|
103
|
+
getFacts() {
|
|
104
|
+
return [...this.state.facts];
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Process events through the engine.
|
|
108
|
+
* Applies all registered rules and checks all registered constraints.
|
|
109
|
+
*
|
|
110
|
+
* @param events Events to process
|
|
111
|
+
* @returns Result with new state and diagnostics
|
|
112
|
+
*/
|
|
113
|
+
step(events) {
|
|
114
|
+
const config = {
|
|
115
|
+
ruleIds: this.registry.getRuleIds(),
|
|
116
|
+
constraintIds: this.registry.getConstraintIds()
|
|
117
|
+
};
|
|
118
|
+
return this.stepWithConfig(events, config);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Process events with specific rule and constraint configuration.
|
|
122
|
+
*
|
|
123
|
+
* @param events Events to process
|
|
124
|
+
* @param config Step configuration
|
|
125
|
+
* @returns Result with new state and diagnostics
|
|
126
|
+
*/
|
|
127
|
+
stepWithConfig(events, config) {
|
|
128
|
+
const diagnostics = [];
|
|
129
|
+
let newState = { ...this.state };
|
|
130
|
+
const newFacts = [];
|
|
131
|
+
for (const ruleId of config.ruleIds) {
|
|
132
|
+
const rule = this.registry.getRule(ruleId);
|
|
133
|
+
if (!rule) {
|
|
134
|
+
diagnostics.push({
|
|
135
|
+
kind: "rule-error",
|
|
136
|
+
message: `Rule "${ruleId}" not found in registry`,
|
|
137
|
+
data: { ruleId }
|
|
138
|
+
});
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const ruleFacts = rule.impl(newState, events);
|
|
143
|
+
newFacts.push(...ruleFacts);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
diagnostics.push({
|
|
146
|
+
kind: "rule-error",
|
|
147
|
+
message: `Error executing rule "${ruleId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
148
|
+
data: { ruleId, error }
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
newState = {
|
|
153
|
+
...newState,
|
|
154
|
+
facts: [...newState.facts, ...newFacts]
|
|
155
|
+
};
|
|
156
|
+
for (const constraintId of config.constraintIds) {
|
|
157
|
+
const constraint = this.registry.getConstraint(constraintId);
|
|
158
|
+
if (!constraint) {
|
|
159
|
+
diagnostics.push({
|
|
160
|
+
kind: "constraint-violation",
|
|
161
|
+
message: `Constraint "${constraintId}" not found in registry`,
|
|
162
|
+
data: { constraintId }
|
|
163
|
+
});
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const result = constraint.impl(newState);
|
|
168
|
+
if (result === false) {
|
|
169
|
+
diagnostics.push({
|
|
170
|
+
kind: "constraint-violation",
|
|
171
|
+
message: `Constraint "${constraintId}" violated`,
|
|
172
|
+
data: { constraintId, description: constraint.description }
|
|
173
|
+
});
|
|
174
|
+
} else if (typeof result === "string") {
|
|
175
|
+
diagnostics.push({
|
|
176
|
+
kind: "constraint-violation",
|
|
177
|
+
message: result,
|
|
178
|
+
data: { constraintId, description: constraint.description }
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
diagnostics.push({
|
|
183
|
+
kind: "constraint-violation",
|
|
184
|
+
message: `Error checking constraint "${constraintId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
185
|
+
data: { constraintId, error }
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
this.state = newState;
|
|
190
|
+
return {
|
|
191
|
+
state: newState,
|
|
192
|
+
diagnostics
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Update the context directly (for exceptional cases).
|
|
197
|
+
* Generally, context should be updated through rules.
|
|
198
|
+
*
|
|
199
|
+
* @param updater Function that produces new context from old context
|
|
200
|
+
*/
|
|
201
|
+
updateContext(updater) {
|
|
202
|
+
this.state = {
|
|
203
|
+
...this.state,
|
|
204
|
+
context: updater(this.state.context)
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Add facts directly (for exceptional cases).
|
|
209
|
+
* Generally, facts should be added through rules.
|
|
210
|
+
*
|
|
211
|
+
* @param facts Facts to add
|
|
212
|
+
*/
|
|
213
|
+
addFacts(facts) {
|
|
214
|
+
this.state = {
|
|
215
|
+
...this.state,
|
|
216
|
+
facts: [...this.state.facts, ...facts]
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Clear all facts
|
|
221
|
+
*/
|
|
222
|
+
clearFacts() {
|
|
223
|
+
this.state = {
|
|
224
|
+
...this.state,
|
|
225
|
+
facts: []
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Reset the engine to initial state
|
|
230
|
+
*/
|
|
231
|
+
reset(options) {
|
|
232
|
+
this.state = {
|
|
233
|
+
context: options.initialContext,
|
|
234
|
+
facts: options.initialFacts ?? [],
|
|
235
|
+
meta: options.initialMeta ?? {},
|
|
236
|
+
protocolVersion: PRAXIS_PROTOCOL_VERSION
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// src/core/pluresdb/adapter.ts
|
|
244
|
+
var adapter_exports = {};
|
|
245
|
+
__export(adapter_exports, {
|
|
246
|
+
InMemoryPraxisDB: () => InMemoryPraxisDB,
|
|
247
|
+
PluresDBPraxisAdapter: () => PluresDBPraxisAdapter,
|
|
248
|
+
createInMemoryDB: () => createInMemoryDB,
|
|
249
|
+
createPluresDB: () => createPluresDB,
|
|
250
|
+
createPraxisLocalFirst: () => createPraxisLocalFirst
|
|
251
|
+
});
|
|
252
|
+
function createInMemoryDB() {
|
|
253
|
+
return new InMemoryPraxisDB();
|
|
254
|
+
}
|
|
255
|
+
function createPluresDB(config) {
|
|
256
|
+
return new PluresDBPraxisAdapter(config);
|
|
257
|
+
}
|
|
258
|
+
async function createPraxisLocalFirst(options = {}) {
|
|
259
|
+
const { pollInterval, ...localOptions } = options;
|
|
260
|
+
const mod = await import("@plures/pluresdb/local-first");
|
|
261
|
+
const LocalFirstCtor = mod.PluresDBLocalFirst ?? mod.default;
|
|
262
|
+
if (!LocalFirstCtor) {
|
|
263
|
+
throw new Error("Failed to load PluresDBLocalFirst from @plures/pluresdb/local-first");
|
|
264
|
+
}
|
|
265
|
+
const db = new LocalFirstCtor(localOptions);
|
|
266
|
+
return new PluresDBPraxisAdapter({ db, pollInterval });
|
|
267
|
+
}
|
|
268
|
+
var InMemoryPraxisDB, PluresDBPraxisAdapter;
|
|
269
|
+
var init_adapter = __esm({
|
|
270
|
+
"src/core/pluresdb/adapter.ts"() {
|
|
271
|
+
"use strict";
|
|
272
|
+
InMemoryPraxisDB = class {
|
|
273
|
+
store = /* @__PURE__ */ new Map();
|
|
274
|
+
watchers = /* @__PURE__ */ new Map();
|
|
275
|
+
async get(key) {
|
|
276
|
+
return this.store.get(key);
|
|
277
|
+
}
|
|
278
|
+
async set(key, value) {
|
|
279
|
+
this.store.set(key, value);
|
|
280
|
+
const keyWatchers = this.watchers.get(key);
|
|
281
|
+
if (keyWatchers) {
|
|
282
|
+
for (const callback of keyWatchers) {
|
|
283
|
+
callback(value);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
watch(key, callback) {
|
|
288
|
+
if (!this.watchers.has(key)) {
|
|
289
|
+
this.watchers.set(key, /* @__PURE__ */ new Set());
|
|
290
|
+
}
|
|
291
|
+
const watchers = this.watchers.get(key);
|
|
292
|
+
const wrappedCallback = (val) => callback(val);
|
|
293
|
+
watchers.add(wrappedCallback);
|
|
294
|
+
return () => {
|
|
295
|
+
watchers.delete(wrappedCallback);
|
|
296
|
+
if (watchers.size === 0) {
|
|
297
|
+
this.watchers.delete(key);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Get all keys (for testing/debugging)
|
|
303
|
+
*/
|
|
304
|
+
keys() {
|
|
305
|
+
return Array.from(this.store.keys());
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Clear all data (for testing)
|
|
309
|
+
*/
|
|
310
|
+
clear() {
|
|
311
|
+
this.store.clear();
|
|
312
|
+
this.watchers.clear();
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
PluresDBPraxisAdapter = class {
|
|
316
|
+
db;
|
|
317
|
+
watchers = /* @__PURE__ */ new Map();
|
|
318
|
+
pollIntervals = /* @__PURE__ */ new Map();
|
|
319
|
+
lastValues = /* @__PURE__ */ new Map();
|
|
320
|
+
pollInterval;
|
|
321
|
+
constructor(config) {
|
|
322
|
+
if ("get" in config && "put" in config) {
|
|
323
|
+
this.db = config;
|
|
324
|
+
this.pollInterval = 1e3;
|
|
325
|
+
} else {
|
|
326
|
+
this.db = config.db;
|
|
327
|
+
this.pollInterval = config.pollInterval ?? 1e3;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async get(key) {
|
|
331
|
+
try {
|
|
332
|
+
const value = await this.db.get(key);
|
|
333
|
+
return value;
|
|
334
|
+
} catch (error) {
|
|
335
|
+
return void 0;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async set(key, value) {
|
|
339
|
+
await this.db.put(key, value);
|
|
340
|
+
this.lastValues.set(key, value);
|
|
341
|
+
const keyWatchers = this.watchers.get(key);
|
|
342
|
+
if (keyWatchers) {
|
|
343
|
+
for (const callback of keyWatchers) {
|
|
344
|
+
callback(value);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
watch(key, callback) {
|
|
349
|
+
if (!this.watchers.has(key)) {
|
|
350
|
+
this.watchers.set(key, /* @__PURE__ */ new Set());
|
|
351
|
+
}
|
|
352
|
+
const watchers = this.watchers.get(key);
|
|
353
|
+
const wrappedCallback = (val) => callback(val);
|
|
354
|
+
watchers.add(wrappedCallback);
|
|
355
|
+
if (!this.pollIntervals.has(key)) {
|
|
356
|
+
const interval = setInterval(async () => {
|
|
357
|
+
try {
|
|
358
|
+
const value = await this.db.get(key);
|
|
359
|
+
const lastValue = this.lastValues.get(key);
|
|
360
|
+
if (JSON.stringify(value) !== JSON.stringify(lastValue)) {
|
|
361
|
+
this.lastValues.set(key, value);
|
|
362
|
+
const currentWatchers = this.watchers.get(key);
|
|
363
|
+
if (currentWatchers) {
|
|
364
|
+
for (const cb of currentWatchers) {
|
|
365
|
+
cb(value);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
} catch (error) {
|
|
370
|
+
}
|
|
371
|
+
}, this.pollInterval);
|
|
372
|
+
this.pollIntervals.set(key, interval);
|
|
373
|
+
}
|
|
374
|
+
return () => {
|
|
375
|
+
watchers.delete(wrappedCallback);
|
|
376
|
+
if (watchers.size === 0) {
|
|
377
|
+
this.watchers.delete(key);
|
|
378
|
+
const interval = this.pollIntervals.get(key);
|
|
379
|
+
if (interval) {
|
|
380
|
+
clearInterval(interval);
|
|
381
|
+
this.pollIntervals.delete(key);
|
|
382
|
+
}
|
|
383
|
+
this.lastValues.delete(key);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Clean up all resources
|
|
389
|
+
*/
|
|
390
|
+
dispose() {
|
|
391
|
+
for (const interval of this.pollIntervals.values()) {
|
|
392
|
+
clearInterval(interval);
|
|
393
|
+
}
|
|
394
|
+
this.pollIntervals.clear();
|
|
395
|
+
this.watchers.clear();
|
|
396
|
+
this.lastValues.clear();
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
30
402
|
// src/index.ts
|
|
31
403
|
var src_exports = {};
|
|
32
404
|
__export(src_exports, {
|
|
405
|
+
AcknowledgeContractGap: () => AcknowledgeContractGap,
|
|
33
406
|
ActorManager: () => ActorManager,
|
|
407
|
+
BehaviorLedger: () => BehaviorLedger,
|
|
408
|
+
ContractAdded: () => ContractAdded,
|
|
409
|
+
ContractGapAcknowledged: () => ContractGapAcknowledged,
|
|
410
|
+
ContractMissing: () => ContractMissing,
|
|
411
|
+
ContractUpdated: () => ContractUpdated,
|
|
412
|
+
ContractValidated: () => ContractValidated,
|
|
34
413
|
FrameworkAgnosticReactiveEngine: () => ReactiveLogicEngine2,
|
|
35
414
|
InMemoryPraxisDB: () => InMemoryPraxisDB,
|
|
36
415
|
LogicEngine: () => LogicEngine,
|
|
@@ -45,12 +424,15 @@ __export(src_exports, {
|
|
|
45
424
|
RegistryIntrospector: () => RegistryIntrospector,
|
|
46
425
|
StateDocsGenerator: () => StateDocsGenerator,
|
|
47
426
|
TerminalAdapter: () => TerminalAdapter,
|
|
427
|
+
ValidateContracts: () => ValidateContracts,
|
|
428
|
+
attachAllIntegrations: () => attachAllIntegrations,
|
|
48
429
|
attachTauriToEngine: () => attachTauriToEngine,
|
|
49
430
|
attachToEngine: () => attachToEngine,
|
|
50
431
|
attachUnumToEngine: () => attachUnumToEngine,
|
|
51
432
|
canvasToMermaid: () => canvasToMermaid,
|
|
52
433
|
canvasToSchema: () => canvasToSchema,
|
|
53
434
|
canvasToYaml: () => canvasToYaml,
|
|
435
|
+
createBehaviorLedger: () => createBehaviorLedger,
|
|
54
436
|
createCanvasEditor: () => createCanvasEditor,
|
|
55
437
|
createFrameworkAgnosticReactiveEngine: () => createReactiveEngine2,
|
|
56
438
|
createInMemoryDB: () => createInMemoryDB,
|
|
@@ -62,6 +444,7 @@ __export(src_exports, {
|
|
|
62
444
|
createPluresDBGenerator: () => createPluresDBGenerator,
|
|
63
445
|
createPraxisDBStore: () => createPraxisDBStore,
|
|
64
446
|
createPraxisEngine: () => createPraxisEngine,
|
|
447
|
+
createPraxisLocalFirst: () => createPraxisLocalFirst,
|
|
65
448
|
createReactiveEngine: () => createReactiveEngine,
|
|
66
449
|
createSchemaRegistry: () => createSchemaRegistry,
|
|
67
450
|
createSchemaTemplate: () => createSchemaTemplate,
|
|
@@ -69,8 +452,10 @@ __export(src_exports, {
|
|
|
69
452
|
createTauriPraxisAdapter: () => createTauriPraxisAdapter,
|
|
70
453
|
createTerminalAdapter: () => createTerminalAdapter,
|
|
71
454
|
createTimerActor: () => createTimerActor,
|
|
455
|
+
createUnifiedApp: () => createUnifiedApp,
|
|
72
456
|
createUnumAdapter: () => createUnumAdapter,
|
|
73
457
|
defineConstraint: () => defineConstraint,
|
|
458
|
+
defineContract: () => defineContract,
|
|
74
459
|
defineEvent: () => defineEvent,
|
|
75
460
|
defineFact: () => defineFact,
|
|
76
461
|
defineModule: () => defineModule,
|
|
@@ -79,12 +464,17 @@ __export(src_exports, {
|
|
|
79
464
|
filterFacts: () => filterFacts,
|
|
80
465
|
findEvent: () => findEvent,
|
|
81
466
|
findFact: () => findFact,
|
|
467
|
+
formatValidationReport: () => formatValidationReport,
|
|
468
|
+
formatValidationReportJSON: () => formatValidationReportJSON,
|
|
469
|
+
formatValidationReportSARIF: () => formatValidationReportSARIF,
|
|
82
470
|
generateDocs: () => generateDocs,
|
|
83
471
|
generateId: () => generateId,
|
|
84
472
|
generateTauriConfig: () => generateTauriConfig,
|
|
473
|
+
getContract: () => getContract,
|
|
85
474
|
getEventPath: () => getEventPath,
|
|
86
475
|
getFactPath: () => getFactPath,
|
|
87
476
|
getSchemaPath: () => getSchemaPath,
|
|
477
|
+
isContract: () => isContract,
|
|
88
478
|
loadSchema: () => loadSchema,
|
|
89
479
|
loadSchemaFromFile: () => loadSchemaFromFile,
|
|
90
480
|
loadSchemaFromJson: () => loadSchemaFromJson,
|
|
@@ -92,19 +482,29 @@ __export(src_exports, {
|
|
|
92
482
|
registerSchema: () => registerSchema,
|
|
93
483
|
runTerminalCommand: () => runTerminalCommand,
|
|
94
484
|
schemaToCanvas: () => schemaToCanvas,
|
|
485
|
+
validateContracts: () => validateContracts,
|
|
95
486
|
validateForGeneration: () => validateForGeneration,
|
|
96
487
|
validateSchema: () => validateSchema,
|
|
97
488
|
validateWithGuardian: () => validateWithGuardian
|
|
98
489
|
});
|
|
99
490
|
module.exports = __toCommonJS(src_exports);
|
|
100
|
-
|
|
101
|
-
// src/core/protocol.ts
|
|
102
|
-
var PRAXIS_PROTOCOL_VERSION = "1.0.0";
|
|
491
|
+
init_protocol();
|
|
103
492
|
|
|
104
493
|
// src/core/rules.ts
|
|
105
494
|
var PraxisRegistry = class {
|
|
106
495
|
rules = /* @__PURE__ */ new Map();
|
|
107
496
|
constraints = /* @__PURE__ */ new Map();
|
|
497
|
+
compliance;
|
|
498
|
+
contractGaps = [];
|
|
499
|
+
constructor(options = {}) {
|
|
500
|
+
const defaultEnabled = typeof process !== "undefined" ? process.env?.NODE_ENV !== "production" : false;
|
|
501
|
+
this.compliance = {
|
|
502
|
+
enabled: defaultEnabled,
|
|
503
|
+
requiredFields: ["behavior", "examples", "invariants"],
|
|
504
|
+
missingSeverity: "warning",
|
|
505
|
+
...options.compliance
|
|
506
|
+
};
|
|
507
|
+
}
|
|
108
508
|
/**
|
|
109
509
|
* Register a rule
|
|
110
510
|
*/
|
|
@@ -113,6 +513,7 @@ var PraxisRegistry = class {
|
|
|
113
513
|
throw new Error(`Rule with id "${descriptor.id}" already registered`);
|
|
114
514
|
}
|
|
115
515
|
this.rules.set(descriptor.id, descriptor);
|
|
516
|
+
this.trackContractCompliance(descriptor.id, descriptor);
|
|
116
517
|
}
|
|
117
518
|
/**
|
|
118
519
|
* Register a constraint
|
|
@@ -122,6 +523,7 @@ var PraxisRegistry = class {
|
|
|
122
523
|
throw new Error(`Constraint with id "${descriptor.id}" already registered`);
|
|
123
524
|
}
|
|
124
525
|
this.constraints.set(descriptor.id, descriptor);
|
|
526
|
+
this.trackContractCompliance(descriptor.id, descriptor);
|
|
125
527
|
}
|
|
126
528
|
/**
|
|
127
529
|
* Register a module (all its rules and constraints)
|
|
@@ -170,199 +572,77 @@ var PraxisRegistry = class {
|
|
|
170
572
|
getAllConstraints() {
|
|
171
573
|
return Array.from(this.constraints.values());
|
|
172
574
|
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// src/core/engine.ts
|
|
176
|
-
function safeClone(value) {
|
|
177
|
-
if (value === null || typeof value !== "object") {
|
|
178
|
-
return value;
|
|
179
|
-
}
|
|
180
|
-
if (typeof globalThis.structuredClone === "function") {
|
|
181
|
-
try {
|
|
182
|
-
return globalThis.structuredClone(value);
|
|
183
|
-
} catch {
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
if (Array.isArray(value)) {
|
|
187
|
-
return [...value];
|
|
188
|
-
}
|
|
189
|
-
return { ...value };
|
|
190
|
-
}
|
|
191
|
-
var LogicEngine = class {
|
|
192
|
-
state;
|
|
193
|
-
registry;
|
|
194
|
-
constructor(options) {
|
|
195
|
-
this.registry = options.registry;
|
|
196
|
-
this.state = {
|
|
197
|
-
context: options.initialContext,
|
|
198
|
-
facts: options.initialFacts ?? [],
|
|
199
|
-
meta: options.initialMeta ?? {},
|
|
200
|
-
protocolVersion: PRAXIS_PROTOCOL_VERSION
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Get the current state (immutable copy)
|
|
205
|
-
*/
|
|
206
|
-
getState() {
|
|
207
|
-
return {
|
|
208
|
-
context: safeClone(this.state.context),
|
|
209
|
-
facts: [...this.state.facts],
|
|
210
|
-
meta: this.state.meta ? safeClone(this.state.meta) : void 0,
|
|
211
|
-
protocolVersion: this.state.protocolVersion
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
575
|
/**
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Get current facts
|
|
222
|
-
*/
|
|
223
|
-
getFacts() {
|
|
224
|
-
return [...this.state.facts];
|
|
576
|
+
* Get collected contract gaps from registration-time validation.
|
|
577
|
+
*/
|
|
578
|
+
getContractGaps() {
|
|
579
|
+
return [...this.contractGaps];
|
|
225
580
|
}
|
|
226
581
|
/**
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
* @returns Result with new state and diagnostics
|
|
232
|
-
*/
|
|
233
|
-
step(events) {
|
|
234
|
-
const config = {
|
|
235
|
-
ruleIds: this.registry.getRuleIds(),
|
|
236
|
-
constraintIds: this.registry.getConstraintIds()
|
|
237
|
-
};
|
|
238
|
-
return this.stepWithConfig(events, config);
|
|
582
|
+
* Clear collected contract gaps.
|
|
583
|
+
*/
|
|
584
|
+
clearContractGaps() {
|
|
585
|
+
this.contractGaps = [];
|
|
239
586
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
* @param events Events to process
|
|
244
|
-
* @param config Step configuration
|
|
245
|
-
* @returns Result with new state and diagnostics
|
|
246
|
-
*/
|
|
247
|
-
stepWithConfig(events, config) {
|
|
248
|
-
const diagnostics = [];
|
|
249
|
-
let newState = { ...this.state };
|
|
250
|
-
const newFacts = [];
|
|
251
|
-
for (const ruleId of config.ruleIds) {
|
|
252
|
-
const rule = this.registry.getRule(ruleId);
|
|
253
|
-
if (!rule) {
|
|
254
|
-
diagnostics.push({
|
|
255
|
-
kind: "rule-error",
|
|
256
|
-
message: `Rule "${ruleId}" not found in registry`,
|
|
257
|
-
data: { ruleId }
|
|
258
|
-
});
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
try {
|
|
262
|
-
const ruleFacts = rule.impl(newState, events);
|
|
263
|
-
newFacts.push(...ruleFacts);
|
|
264
|
-
} catch (error) {
|
|
265
|
-
diagnostics.push({
|
|
266
|
-
kind: "rule-error",
|
|
267
|
-
message: `Error executing rule "${ruleId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
268
|
-
data: { ruleId, error }
|
|
269
|
-
});
|
|
270
|
-
}
|
|
587
|
+
trackContractCompliance(id, descriptor) {
|
|
588
|
+
if (!this.compliance.enabled) {
|
|
589
|
+
return;
|
|
271
590
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
kind: "constraint-violation",
|
|
281
|
-
message: `Constraint "${constraintId}" not found in registry`,
|
|
282
|
-
data: { constraintId }
|
|
283
|
-
});
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
try {
|
|
287
|
-
const result = constraint.impl(newState);
|
|
288
|
-
if (result === false) {
|
|
289
|
-
diagnostics.push({
|
|
290
|
-
kind: "constraint-violation",
|
|
291
|
-
message: `Constraint "${constraintId}" violated`,
|
|
292
|
-
data: { constraintId, description: constraint.description }
|
|
293
|
-
});
|
|
294
|
-
} else if (typeof result === "string") {
|
|
295
|
-
diagnostics.push({
|
|
296
|
-
kind: "constraint-violation",
|
|
297
|
-
message: result,
|
|
298
|
-
data: { constraintId, description: constraint.description }
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
} catch (error) {
|
|
302
|
-
diagnostics.push({
|
|
303
|
-
kind: "constraint-violation",
|
|
304
|
-
message: `Error checking constraint "${constraintId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
305
|
-
data: { constraintId, error }
|
|
306
|
-
});
|
|
591
|
+
const gaps = this.validateDescriptorContract(id, descriptor);
|
|
592
|
+
for (const gap of gaps) {
|
|
593
|
+
this.contractGaps.push(gap);
|
|
594
|
+
if (this.compliance.onGap) {
|
|
595
|
+
this.compliance.onGap(gap);
|
|
596
|
+
} else {
|
|
597
|
+
const label = gap.severity === "error" ? "ERROR" : gap.severity === "warning" ? "WARN" : "INFO";
|
|
598
|
+
console.warn(`[Praxis][${label}] Contract gap for "${gap.ruleId}": missing ${gap.missing.join(", ")}`);
|
|
307
599
|
}
|
|
308
600
|
}
|
|
309
|
-
this.state = newState;
|
|
310
|
-
return {
|
|
311
|
-
state: newState,
|
|
312
|
-
diagnostics
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
/**
|
|
316
|
-
* Update the context directly (for exceptional cases).
|
|
317
|
-
* Generally, context should be updated through rules.
|
|
318
|
-
*
|
|
319
|
-
* @param updater Function that produces new context from old context
|
|
320
|
-
*/
|
|
321
|
-
updateContext(updater) {
|
|
322
|
-
this.state = {
|
|
323
|
-
...this.state,
|
|
324
|
-
context: updater(this.state.context)
|
|
325
|
-
};
|
|
326
601
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
602
|
+
validateDescriptorContract(id, descriptor) {
|
|
603
|
+
const requiredFields = this.compliance.requiredFields ?? ["behavior", "examples", "invariants"];
|
|
604
|
+
const missingSeverity = this.compliance.missingSeverity ?? "warning";
|
|
605
|
+
const contract = descriptor.contract ?? (descriptor.meta?.contract && typeof descriptor.meta.contract === "object" ? descriptor.meta.contract : void 0);
|
|
606
|
+
if (!contract) {
|
|
607
|
+
return [
|
|
608
|
+
{
|
|
609
|
+
ruleId: id,
|
|
610
|
+
missing: ["contract"],
|
|
611
|
+
severity: missingSeverity,
|
|
612
|
+
message: `Contract missing for "${id}"`
|
|
613
|
+
}
|
|
614
|
+
];
|
|
615
|
+
}
|
|
616
|
+
const missing = [];
|
|
617
|
+
if (requiredFields.includes("behavior") && (!contract.behavior || contract.behavior.trim() === "")) {
|
|
618
|
+
missing.push("behavior");
|
|
619
|
+
}
|
|
620
|
+
if (requiredFields.includes("examples") && (!contract.examples || contract.examples.length === 0)) {
|
|
621
|
+
missing.push("examples");
|
|
622
|
+
}
|
|
623
|
+
if (requiredFields.includes("invariants") && (!contract.invariants || contract.invariants.length === 0)) {
|
|
624
|
+
missing.push("invariants");
|
|
625
|
+
}
|
|
626
|
+
if (missing.length === 0) {
|
|
627
|
+
return [];
|
|
628
|
+
}
|
|
629
|
+
return [
|
|
630
|
+
{
|
|
631
|
+
ruleId: id,
|
|
632
|
+
missing,
|
|
633
|
+
severity: "warning",
|
|
634
|
+
message: `Contract for "${id}" is incomplete: missing ${missing.join(", ")}`
|
|
635
|
+
}
|
|
636
|
+
];
|
|
358
637
|
}
|
|
359
638
|
};
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
639
|
+
|
|
640
|
+
// src/index.ts
|
|
641
|
+
init_engine();
|
|
363
642
|
|
|
364
643
|
// src/core/reactive-engine.svelte.ts
|
|
365
644
|
var $ = __toESM(require("svelte/internal/client"), 1);
|
|
645
|
+
init_engine();
|
|
366
646
|
var ReactiveLogicEngine = class {
|
|
367
647
|
#state = (
|
|
368
648
|
// Use Svelte's $state rune for automatic reactivity
|
|
@@ -999,19 +1279,25 @@ function defineEvent(tag) {
|
|
|
999
1279
|
};
|
|
1000
1280
|
}
|
|
1001
1281
|
function defineRule(options) {
|
|
1282
|
+
const contract = options.contract ?? options.meta?.contract;
|
|
1283
|
+
const meta = contract ? { ...options.meta ?? {}, contract } : options.meta;
|
|
1002
1284
|
return {
|
|
1003
1285
|
id: options.id,
|
|
1004
1286
|
description: options.description,
|
|
1005
1287
|
impl: options.impl,
|
|
1006
|
-
|
|
1288
|
+
contract,
|
|
1289
|
+
meta
|
|
1007
1290
|
};
|
|
1008
1291
|
}
|
|
1009
1292
|
function defineConstraint(options) {
|
|
1293
|
+
const contract = options.contract ?? options.meta?.contract;
|
|
1294
|
+
const meta = contract ? { ...options.meta ?? {}, contract } : options.meta;
|
|
1010
1295
|
return {
|
|
1011
1296
|
id: options.id,
|
|
1012
1297
|
description: options.description,
|
|
1013
1298
|
impl: options.impl,
|
|
1014
|
-
|
|
1299
|
+
contract,
|
|
1300
|
+
meta
|
|
1015
1301
|
};
|
|
1016
1302
|
}
|
|
1017
1303
|
function defineModule(options) {
|
|
@@ -1034,6 +1320,461 @@ function findFact(facts, definition) {
|
|
|
1034
1320
|
return facts.find(definition.is);
|
|
1035
1321
|
}
|
|
1036
1322
|
|
|
1323
|
+
// src/decision-ledger/types.ts
|
|
1324
|
+
function isContract(obj) {
|
|
1325
|
+
if (typeof obj !== "object" || obj === null) {
|
|
1326
|
+
return false;
|
|
1327
|
+
}
|
|
1328
|
+
const contract = obj;
|
|
1329
|
+
return typeof contract.ruleId === "string" && typeof contract.behavior === "string" && Array.isArray(contract.examples) && contract.examples.length > 0 && contract.examples.every(
|
|
1330
|
+
(ex) => typeof ex === "object" && ex !== null && typeof ex.given === "string" && typeof ex.when === "string" && typeof ex.then === "string"
|
|
1331
|
+
) && Array.isArray(contract.invariants) && contract.invariants.every((inv) => typeof inv === "string");
|
|
1332
|
+
}
|
|
1333
|
+
function defineContract(options) {
|
|
1334
|
+
if (options.examples.length === 0) {
|
|
1335
|
+
throw new Error("Contract must have at least one example");
|
|
1336
|
+
}
|
|
1337
|
+
if (options.assumptions) {
|
|
1338
|
+
for (const assumption of options.assumptions) {
|
|
1339
|
+
if (assumption.confidence < 0 || assumption.confidence > 1) {
|
|
1340
|
+
throw new Error(
|
|
1341
|
+
`Assumption '${assumption.id}' has invalid confidence value ${assumption.confidence}. Must be between 0.0 and 1.0`
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
return {
|
|
1347
|
+
ruleId: options.ruleId,
|
|
1348
|
+
behavior: options.behavior,
|
|
1349
|
+
examples: options.examples,
|
|
1350
|
+
invariants: options.invariants,
|
|
1351
|
+
assumptions: options.assumptions,
|
|
1352
|
+
references: options.references,
|
|
1353
|
+
version: options.version || "1.0.0",
|
|
1354
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
function getContract(meta) {
|
|
1358
|
+
if (!meta || !meta.contract) {
|
|
1359
|
+
return void 0;
|
|
1360
|
+
}
|
|
1361
|
+
if (isContract(meta.contract)) {
|
|
1362
|
+
return meta.contract;
|
|
1363
|
+
}
|
|
1364
|
+
return void 0;
|
|
1365
|
+
}
|
|
1366
|
+
function getContractFromDescriptor(descriptor) {
|
|
1367
|
+
if (!descriptor) {
|
|
1368
|
+
return void 0;
|
|
1369
|
+
}
|
|
1370
|
+
if (descriptor.contract && isContract(descriptor.contract)) {
|
|
1371
|
+
return descriptor.contract;
|
|
1372
|
+
}
|
|
1373
|
+
return getContract(descriptor.meta);
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
// src/decision-ledger/facts-events.ts
|
|
1377
|
+
var ContractMissing = defineFact("ContractMissing");
|
|
1378
|
+
var ContractValidated = defineFact("ContractValidated");
|
|
1379
|
+
var AcknowledgeContractGap = defineEvent("ACKNOWLEDGE_CONTRACT_GAP");
|
|
1380
|
+
var ValidateContracts = defineEvent("VALIDATE_CONTRACTS");
|
|
1381
|
+
var ContractGapAcknowledged = defineFact("ContractGapAcknowledged");
|
|
1382
|
+
var ContractAdded = defineEvent("CONTRACT_ADDED");
|
|
1383
|
+
var ContractUpdated = defineEvent("CONTRACT_UPDATED");
|
|
1384
|
+
var ContractGapEmitted = defineEvent("CONTRACT_GAP_EMITTED");
|
|
1385
|
+
|
|
1386
|
+
// src/decision-ledger/validation.ts
|
|
1387
|
+
function validateContracts(registry, options = {}) {
|
|
1388
|
+
const {
|
|
1389
|
+
incompleteSeverity = "warning",
|
|
1390
|
+
requiredFields = ["behavior", "examples"],
|
|
1391
|
+
artifactIndex
|
|
1392
|
+
} = options;
|
|
1393
|
+
const complete = [];
|
|
1394
|
+
const incomplete = [];
|
|
1395
|
+
const missing = [];
|
|
1396
|
+
for (const rule of registry.getAllRules()) {
|
|
1397
|
+
const contract = getContractFromDescriptor(rule);
|
|
1398
|
+
if (!contract) {
|
|
1399
|
+
missing.push(rule.id);
|
|
1400
|
+
if (options.missingSeverity) {
|
|
1401
|
+
incomplete.push({
|
|
1402
|
+
ruleId: rule.id,
|
|
1403
|
+
missing: ["contract"],
|
|
1404
|
+
severity: options.missingSeverity,
|
|
1405
|
+
message: `Rule '${rule.id}' has no contract`
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
continue;
|
|
1409
|
+
}
|
|
1410
|
+
const gaps = validateContract(contract, requiredFields, artifactIndex);
|
|
1411
|
+
if (gaps.length > 0) {
|
|
1412
|
+
incomplete.push({
|
|
1413
|
+
ruleId: rule.id,
|
|
1414
|
+
missing: gaps,
|
|
1415
|
+
severity: incompleteSeverity,
|
|
1416
|
+
message: `Rule '${rule.id}' contract is incomplete: missing ${gaps.join(", ")}`
|
|
1417
|
+
});
|
|
1418
|
+
} else {
|
|
1419
|
+
complete.push({ ruleId: rule.id, contract });
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
for (const constraint of registry.getAllConstraints()) {
|
|
1423
|
+
const contract = getContractFromDescriptor(constraint);
|
|
1424
|
+
if (!contract) {
|
|
1425
|
+
missing.push(constraint.id);
|
|
1426
|
+
if (options.missingSeverity) {
|
|
1427
|
+
incomplete.push({
|
|
1428
|
+
ruleId: constraint.id,
|
|
1429
|
+
missing: ["contract"],
|
|
1430
|
+
severity: options.missingSeverity,
|
|
1431
|
+
message: `Constraint '${constraint.id}' has no contract`
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
continue;
|
|
1435
|
+
}
|
|
1436
|
+
const gaps = validateContract(contract, requiredFields, artifactIndex);
|
|
1437
|
+
if (gaps.length > 0) {
|
|
1438
|
+
incomplete.push({
|
|
1439
|
+
ruleId: constraint.id,
|
|
1440
|
+
missing: gaps,
|
|
1441
|
+
severity: incompleteSeverity,
|
|
1442
|
+
message: `Constraint '${constraint.id}' contract is incomplete: missing ${gaps.join(", ")}`
|
|
1443
|
+
});
|
|
1444
|
+
} else {
|
|
1445
|
+
complete.push({ ruleId: constraint.id, contract });
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
const total = registry.getAllRules().length + registry.getAllConstraints().length;
|
|
1449
|
+
return {
|
|
1450
|
+
complete,
|
|
1451
|
+
incomplete,
|
|
1452
|
+
missing,
|
|
1453
|
+
total,
|
|
1454
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
function validateContract(contract, requiredFields, artifactIndex) {
|
|
1458
|
+
const missing = [];
|
|
1459
|
+
if (requiredFields.includes("behavior") && isFieldEmpty(contract.behavior)) {
|
|
1460
|
+
missing.push("behavior");
|
|
1461
|
+
}
|
|
1462
|
+
if (requiredFields.includes("examples") && (!contract.examples || contract.examples.length === 0)) {
|
|
1463
|
+
missing.push("examples");
|
|
1464
|
+
}
|
|
1465
|
+
if (requiredFields.includes("invariants") && (!contract.invariants || contract.invariants.length === 0)) {
|
|
1466
|
+
missing.push("invariants");
|
|
1467
|
+
}
|
|
1468
|
+
if (artifactIndex?.tests && !artifactIndex.tests.has(contract.ruleId)) {
|
|
1469
|
+
missing.push("tests");
|
|
1470
|
+
}
|
|
1471
|
+
if (artifactIndex?.spec && !artifactIndex.spec.has(contract.ruleId)) {
|
|
1472
|
+
missing.push("spec");
|
|
1473
|
+
}
|
|
1474
|
+
return missing;
|
|
1475
|
+
}
|
|
1476
|
+
function isFieldEmpty(value) {
|
|
1477
|
+
return !value || value.trim() === "";
|
|
1478
|
+
}
|
|
1479
|
+
function formatValidationReport(report) {
|
|
1480
|
+
const lines = [];
|
|
1481
|
+
lines.push("Contract Validation Report");
|
|
1482
|
+
lines.push("=".repeat(50));
|
|
1483
|
+
lines.push("");
|
|
1484
|
+
lines.push(`Total: ${report.total}`);
|
|
1485
|
+
lines.push(`Complete: ${report.complete.length}`);
|
|
1486
|
+
lines.push(`Incomplete: ${report.incomplete.length}`);
|
|
1487
|
+
lines.push(`Missing: ${report.missing.length}`);
|
|
1488
|
+
lines.push("");
|
|
1489
|
+
if (report.complete.length > 0) {
|
|
1490
|
+
lines.push("\u2713 Complete Contracts:");
|
|
1491
|
+
for (const { ruleId, contract } of report.complete) {
|
|
1492
|
+
lines.push(` \u2713 ${ruleId} (v${contract.version || "1.0.0"})`);
|
|
1493
|
+
}
|
|
1494
|
+
lines.push("");
|
|
1495
|
+
}
|
|
1496
|
+
if (report.incomplete.length > 0) {
|
|
1497
|
+
lines.push("\u2717 Incomplete Contracts:");
|
|
1498
|
+
for (const gap of report.incomplete) {
|
|
1499
|
+
const icon = gap.severity === "error" ? "\u2717" : gap.severity === "warning" ? "\u26A0" : "\u2139";
|
|
1500
|
+
lines.push(` ${icon} ${gap.ruleId} - Missing: ${gap.missing.join(", ")}`);
|
|
1501
|
+
if (gap.message) {
|
|
1502
|
+
lines.push(` ${gap.message}`);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
lines.push("");
|
|
1506
|
+
}
|
|
1507
|
+
if (report.missing.length > 0) {
|
|
1508
|
+
lines.push("\u2717 No Contract:");
|
|
1509
|
+
for (const ruleId of report.missing) {
|
|
1510
|
+
lines.push(` \u2717 ${ruleId}`);
|
|
1511
|
+
}
|
|
1512
|
+
lines.push("");
|
|
1513
|
+
}
|
|
1514
|
+
lines.push(`Validated at: ${report.timestamp}`);
|
|
1515
|
+
return lines.join("\n");
|
|
1516
|
+
}
|
|
1517
|
+
function formatValidationReportJSON(report) {
|
|
1518
|
+
return JSON.stringify(report, null, 2);
|
|
1519
|
+
}
|
|
1520
|
+
function formatValidationReportSARIF(report) {
|
|
1521
|
+
const results = report.incomplete.map((gap) => {
|
|
1522
|
+
const primaryMissing = gap.missing.length > 0 ? gap.missing[0] : "contract";
|
|
1523
|
+
return {
|
|
1524
|
+
ruleId: `decision-ledger/${primaryMissing}`,
|
|
1525
|
+
level: gap.severity === "error" ? "error" : gap.severity === "warning" ? "warning" : "note",
|
|
1526
|
+
message: {
|
|
1527
|
+
text: gap.message || `Missing: ${gap.missing.join(", ")}`
|
|
1528
|
+
},
|
|
1529
|
+
locations: [
|
|
1530
|
+
{
|
|
1531
|
+
physicalLocation: {
|
|
1532
|
+
artifactLocation: {
|
|
1533
|
+
uri: "registry"
|
|
1534
|
+
},
|
|
1535
|
+
region: {
|
|
1536
|
+
startLine: 1
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
],
|
|
1541
|
+
properties: {
|
|
1542
|
+
ruleId: gap.ruleId,
|
|
1543
|
+
missing: gap.missing
|
|
1544
|
+
}
|
|
1545
|
+
};
|
|
1546
|
+
});
|
|
1547
|
+
const sarif = {
|
|
1548
|
+
version: "2.1.0",
|
|
1549
|
+
$schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
|
1550
|
+
runs: [
|
|
1551
|
+
{
|
|
1552
|
+
tool: {
|
|
1553
|
+
driver: {
|
|
1554
|
+
name: "Praxis Decision Ledger",
|
|
1555
|
+
version: "1.0.0",
|
|
1556
|
+
informationUri: "https://github.com/plures/praxis",
|
|
1557
|
+
rules: [
|
|
1558
|
+
{
|
|
1559
|
+
id: "decision-ledger/contract",
|
|
1560
|
+
shortDescription: {
|
|
1561
|
+
text: "Rule or constraint missing contract"
|
|
1562
|
+
}
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
id: "decision-ledger/behavior",
|
|
1566
|
+
shortDescription: {
|
|
1567
|
+
text: "Contract missing behavior description"
|
|
1568
|
+
}
|
|
1569
|
+
},
|
|
1570
|
+
{
|
|
1571
|
+
id: "decision-ledger/examples",
|
|
1572
|
+
shortDescription: {
|
|
1573
|
+
text: "Contract missing examples"
|
|
1574
|
+
}
|
|
1575
|
+
},
|
|
1576
|
+
{
|
|
1577
|
+
id: "decision-ledger/invariants",
|
|
1578
|
+
shortDescription: {
|
|
1579
|
+
text: "Contract missing invariants"
|
|
1580
|
+
}
|
|
1581
|
+
},
|
|
1582
|
+
{
|
|
1583
|
+
id: "decision-ledger/tests",
|
|
1584
|
+
shortDescription: {
|
|
1585
|
+
text: "Contract missing tests"
|
|
1586
|
+
}
|
|
1587
|
+
},
|
|
1588
|
+
{
|
|
1589
|
+
id: "decision-ledger/spec",
|
|
1590
|
+
shortDescription: {
|
|
1591
|
+
text: "Contract missing spec"
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
]
|
|
1595
|
+
}
|
|
1596
|
+
},
|
|
1597
|
+
results
|
|
1598
|
+
}
|
|
1599
|
+
]
|
|
1600
|
+
};
|
|
1601
|
+
return JSON.stringify(sarif, null, 2);
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// src/decision-ledger/ledger.ts
|
|
1605
|
+
var BehaviorLedger = class _BehaviorLedger {
|
|
1606
|
+
entries = [];
|
|
1607
|
+
entryMap = /* @__PURE__ */ new Map();
|
|
1608
|
+
/**
|
|
1609
|
+
* Append a new entry to the ledger.
|
|
1610
|
+
*
|
|
1611
|
+
* @param entry The entry to append
|
|
1612
|
+
* @throws Error if entry ID already exists
|
|
1613
|
+
*/
|
|
1614
|
+
append(entry) {
|
|
1615
|
+
if (this.entryMap.has(entry.id)) {
|
|
1616
|
+
throw new Error(`Ledger entry with ID '${entry.id}' already exists`);
|
|
1617
|
+
}
|
|
1618
|
+
if (entry.supersedes) {
|
|
1619
|
+
const superseded = this.entryMap.get(entry.supersedes);
|
|
1620
|
+
if (superseded && superseded.status === "active") {
|
|
1621
|
+
const updatedEntry = {
|
|
1622
|
+
...superseded,
|
|
1623
|
+
status: "superseded"
|
|
1624
|
+
};
|
|
1625
|
+
this.entryMap.set(entry.supersedes, updatedEntry);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
this.entries.push(entry);
|
|
1629
|
+
this.entryMap.set(entry.id, entry);
|
|
1630
|
+
}
|
|
1631
|
+
/**
|
|
1632
|
+
* Get an entry by ID.
|
|
1633
|
+
*
|
|
1634
|
+
* @param id The entry ID
|
|
1635
|
+
* @returns The entry, or undefined if not found
|
|
1636
|
+
*/
|
|
1637
|
+
getEntry(id) {
|
|
1638
|
+
return this.entryMap.get(id);
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Get all entries (in order of append) with current status.
|
|
1642
|
+
*
|
|
1643
|
+
* @returns Array of all entries with current status from the map
|
|
1644
|
+
*/
|
|
1645
|
+
getAllEntries() {
|
|
1646
|
+
return this.entries.map((entry) => this.entryMap.get(entry.id));
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* Get entries for a specific rule ID.
|
|
1650
|
+
*
|
|
1651
|
+
* @param ruleId The rule ID
|
|
1652
|
+
* @returns Array of entries for this rule with current status
|
|
1653
|
+
*/
|
|
1654
|
+
getEntriesForRule(ruleId) {
|
|
1655
|
+
return this.entries.map((entry) => this.entryMap.get(entry.id)).filter((entry) => entry.contract.ruleId === ruleId);
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Get the latest active entry for a rule.
|
|
1659
|
+
*
|
|
1660
|
+
* @param ruleId The rule ID
|
|
1661
|
+
* @returns The latest active entry, or undefined if none
|
|
1662
|
+
*/
|
|
1663
|
+
getLatestEntry(ruleId) {
|
|
1664
|
+
const entries = this.getEntriesForRule(ruleId);
|
|
1665
|
+
const activeEntries = entries.filter((entry) => entry.status === "active");
|
|
1666
|
+
if (activeEntries.length === 0) {
|
|
1667
|
+
return void 0;
|
|
1668
|
+
}
|
|
1669
|
+
return activeEntries[activeEntries.length - 1];
|
|
1670
|
+
}
|
|
1671
|
+
/**
|
|
1672
|
+
* Get all active assumptions across all entries.
|
|
1673
|
+
*
|
|
1674
|
+
* @returns Map of assumption ID to assumption
|
|
1675
|
+
*/
|
|
1676
|
+
getActiveAssumptions() {
|
|
1677
|
+
const assumptions = /* @__PURE__ */ new Map();
|
|
1678
|
+
for (const entry of this.entries) {
|
|
1679
|
+
const currentEntry = this.entryMap.get(entry.id);
|
|
1680
|
+
if (currentEntry.status !== "active") {
|
|
1681
|
+
continue;
|
|
1682
|
+
}
|
|
1683
|
+
for (const assumption of currentEntry.contract.assumptions || []) {
|
|
1684
|
+
if (assumption.status === "active") {
|
|
1685
|
+
assumptions.set(assumption.id, assumption);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
return assumptions;
|
|
1690
|
+
}
|
|
1691
|
+
/**
|
|
1692
|
+
* Find assumptions that impact a specific artifact type.
|
|
1693
|
+
*
|
|
1694
|
+
* @param impactType The artifact type ('spec', 'tests', 'code')
|
|
1695
|
+
* @returns Array of assumptions
|
|
1696
|
+
*/
|
|
1697
|
+
findAssumptionsByImpact(impactType) {
|
|
1698
|
+
const assumptions = [];
|
|
1699
|
+
for (const entry of this.entries) {
|
|
1700
|
+
const currentEntry = this.entryMap.get(entry.id);
|
|
1701
|
+
if (currentEntry.status !== "active") {
|
|
1702
|
+
continue;
|
|
1703
|
+
}
|
|
1704
|
+
for (const assumption of currentEntry.contract.assumptions || []) {
|
|
1705
|
+
if (assumption.status === "active" && assumption.impacts.includes(impactType)) {
|
|
1706
|
+
assumptions.push(assumption);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
return assumptions;
|
|
1711
|
+
}
|
|
1712
|
+
/**
|
|
1713
|
+
* Get ledger statistics.
|
|
1714
|
+
*/
|
|
1715
|
+
getStats() {
|
|
1716
|
+
const currentEntries = this.entries.map((e) => this.entryMap.get(e.id));
|
|
1717
|
+
const active = currentEntries.filter((e) => e.status === "active").length;
|
|
1718
|
+
const superseded = currentEntries.filter((e) => e.status === "superseded").length;
|
|
1719
|
+
const deprecated = currentEntries.filter((e) => e.status === "deprecated").length;
|
|
1720
|
+
const uniqueRules = new Set(currentEntries.map((e) => e.contract.ruleId)).size;
|
|
1721
|
+
return {
|
|
1722
|
+
totalEntries: this.entries.length,
|
|
1723
|
+
activeEntries: active,
|
|
1724
|
+
supersededEntries: superseded,
|
|
1725
|
+
deprecatedEntries: deprecated,
|
|
1726
|
+
uniqueRules
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Export ledger as JSON.
|
|
1731
|
+
*
|
|
1732
|
+
* @returns JSON string with current entry status
|
|
1733
|
+
*/
|
|
1734
|
+
toJSON() {
|
|
1735
|
+
return JSON.stringify(
|
|
1736
|
+
{
|
|
1737
|
+
version: "1.0.0",
|
|
1738
|
+
// Export entries with current status from the map
|
|
1739
|
+
entries: this.entries.map((entry) => this.entryMap.get(entry.id)),
|
|
1740
|
+
stats: this.getStats()
|
|
1741
|
+
},
|
|
1742
|
+
null,
|
|
1743
|
+
2
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
/**
|
|
1747
|
+
* Import ledger from JSON.
|
|
1748
|
+
*
|
|
1749
|
+
* Note: The JSON must contain entries in the order they were originally appended.
|
|
1750
|
+
* If a superseding entry appears before the entry it supersedes, the superseding
|
|
1751
|
+
* logic will not work correctly. The toJSON method preserves this order.
|
|
1752
|
+
*
|
|
1753
|
+
* @param json The JSON string
|
|
1754
|
+
* @returns A new BehaviorLedger instance
|
|
1755
|
+
*/
|
|
1756
|
+
static fromJSON(json) {
|
|
1757
|
+
const data = JSON.parse(json);
|
|
1758
|
+
const ledger = new _BehaviorLedger();
|
|
1759
|
+
for (const entry of data.entries || []) {
|
|
1760
|
+
ledger.append(entry);
|
|
1761
|
+
}
|
|
1762
|
+
return ledger;
|
|
1763
|
+
}
|
|
1764
|
+
};
|
|
1765
|
+
function createBehaviorLedger() {
|
|
1766
|
+
return new BehaviorLedger();
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
// src/decision-ledger/logic-ledger.ts
|
|
1770
|
+
var import_node_crypto = require("crypto");
|
|
1771
|
+
var import_node_fs = require("fs");
|
|
1772
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
1773
|
+
|
|
1774
|
+
// src/decision-ledger/scanner.ts
|
|
1775
|
+
var import_node_fs2 = require("fs");
|
|
1776
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
1777
|
+
|
|
1037
1778
|
// src/runtime/terminal-adapter.ts
|
|
1038
1779
|
async function defaultExecutor(command, options) {
|
|
1039
1780
|
try {
|
|
@@ -1154,15 +1895,15 @@ var TerminalAdapter = class {
|
|
|
1154
1895
|
* @param path - PluresDB path for storing results
|
|
1155
1896
|
* @param data - Data to sync
|
|
1156
1897
|
*/
|
|
1157
|
-
async syncToPluresDB(
|
|
1898
|
+
async syncToPluresDB(path3, data) {
|
|
1158
1899
|
if (!this.db) return;
|
|
1159
1900
|
try {
|
|
1160
|
-
await this.db.set(
|
|
1901
|
+
await this.db.set(path3, {
|
|
1161
1902
|
...data,
|
|
1162
1903
|
nodeId: this.state.nodeId,
|
|
1163
1904
|
syncedAt: Date.now()
|
|
1164
1905
|
});
|
|
1165
|
-
const historyPath = `${
|
|
1906
|
+
const historyPath = `${path3}/history`;
|
|
1166
1907
|
const historyKey = `${historyPath}/${data.timestamp}`;
|
|
1167
1908
|
await this.db.set(historyKey, data);
|
|
1168
1909
|
} catch (error) {
|
|
@@ -1631,8 +2372,8 @@ var PraxisDBStore = class {
|
|
|
1631
2372
|
async persistFact(fact) {
|
|
1632
2373
|
const payload = fact.payload;
|
|
1633
2374
|
const id = payload?.id ?? generateId();
|
|
1634
|
-
const
|
|
1635
|
-
await this.db.set(
|
|
2375
|
+
const path3 = getFactPath(fact.tag, id);
|
|
2376
|
+
await this.db.set(path3, fact);
|
|
1636
2377
|
}
|
|
1637
2378
|
/**
|
|
1638
2379
|
* Get a fact by tag and id
|
|
@@ -1642,8 +2383,8 @@ var PraxisDBStore = class {
|
|
|
1642
2383
|
* @returns The fact or undefined if not found
|
|
1643
2384
|
*/
|
|
1644
2385
|
async getFact(factTag, id) {
|
|
1645
|
-
const
|
|
1646
|
-
return this.db.get(
|
|
2386
|
+
const path3 = getFactPath(factTag, id);
|
|
2387
|
+
return this.db.get(path3);
|
|
1647
2388
|
}
|
|
1648
2389
|
/**
|
|
1649
2390
|
* Append an event to the event stream
|
|
@@ -1653,15 +2394,15 @@ var PraxisDBStore = class {
|
|
|
1653
2394
|
* @param event The event to append
|
|
1654
2395
|
*/
|
|
1655
2396
|
async appendEvent(event) {
|
|
1656
|
-
const
|
|
1657
|
-
const existingEvents = await this.db.get(
|
|
2397
|
+
const path3 = getEventPath(event.tag);
|
|
2398
|
+
const existingEvents = await this.db.get(path3) ?? [];
|
|
1658
2399
|
const entry = {
|
|
1659
2400
|
event,
|
|
1660
2401
|
timestamp: Date.now(),
|
|
1661
2402
|
sequence: existingEvents.length
|
|
1662
2403
|
};
|
|
1663
2404
|
const newEvents = [...existingEvents, entry];
|
|
1664
|
-
await this.db.set(
|
|
2405
|
+
await this.db.set(path3, newEvents);
|
|
1665
2406
|
await this.triggerRulesForEvents([event]);
|
|
1666
2407
|
}
|
|
1667
2408
|
/**
|
|
@@ -1676,15 +2417,15 @@ var PraxisDBStore = class {
|
|
|
1676
2417
|
eventsByTag.set(event.tag, [...existing, event]);
|
|
1677
2418
|
}
|
|
1678
2419
|
for (const [tag, tagEvents] of eventsByTag) {
|
|
1679
|
-
const
|
|
1680
|
-
const existingEvents = await this.db.get(
|
|
2420
|
+
const path3 = getEventPath(tag);
|
|
2421
|
+
const existingEvents = await this.db.get(path3) ?? [];
|
|
1681
2422
|
let sequence = existingEvents.length;
|
|
1682
2423
|
const newEntries = tagEvents.map((event) => ({
|
|
1683
2424
|
event,
|
|
1684
2425
|
timestamp: Date.now(),
|
|
1685
2426
|
sequence: sequence++
|
|
1686
2427
|
}));
|
|
1687
|
-
await this.db.set(
|
|
2428
|
+
await this.db.set(path3, [...existingEvents, ...newEntries]);
|
|
1688
2429
|
}
|
|
1689
2430
|
await this.triggerRulesForEvents(events);
|
|
1690
2431
|
}
|
|
@@ -1696,8 +2437,8 @@ var PraxisDBStore = class {
|
|
|
1696
2437
|
* @returns Array of event stream entries
|
|
1697
2438
|
*/
|
|
1698
2439
|
async getEvents(eventTag, options) {
|
|
1699
|
-
const
|
|
1700
|
-
const events = await this.db.get(
|
|
2440
|
+
const path3 = getEventPath(eventTag);
|
|
2441
|
+
const events = await this.db.get(path3) ?? [];
|
|
1701
2442
|
let result = events;
|
|
1702
2443
|
if (options?.since !== void 0) {
|
|
1703
2444
|
const sinceTimestamp = options.since;
|
|
@@ -1716,7 +2457,7 @@ var PraxisDBStore = class {
|
|
|
1716
2457
|
* @returns Unsubscribe function
|
|
1717
2458
|
*/
|
|
1718
2459
|
watchFacts(factTag, callback) {
|
|
1719
|
-
const
|
|
2460
|
+
const path3 = getFactPath(factTag);
|
|
1720
2461
|
if (!this.factWatchers.has(factTag)) {
|
|
1721
2462
|
this.factWatchers.set(factTag, /* @__PURE__ */ new Set());
|
|
1722
2463
|
}
|
|
@@ -1724,7 +2465,7 @@ var PraxisDBStore = class {
|
|
|
1724
2465
|
if (watchers) {
|
|
1725
2466
|
watchers.add(callback);
|
|
1726
2467
|
}
|
|
1727
|
-
const unsubscribe = this.db.watch(
|
|
2468
|
+
const unsubscribe = this.db.watch(path3, (fact) => {
|
|
1728
2469
|
callback([fact]);
|
|
1729
2470
|
});
|
|
1730
2471
|
this.subscriptions.push(unsubscribe);
|
|
@@ -1830,140 +2571,8 @@ function createPraxisDBStore(db, registry, initialContext, onRuleError) {
|
|
|
1830
2571
|
return new PraxisDBStore({ db, registry, initialContext, onRuleError });
|
|
1831
2572
|
}
|
|
1832
2573
|
|
|
1833
|
-
// src/
|
|
1834
|
-
|
|
1835
|
-
store = /* @__PURE__ */ new Map();
|
|
1836
|
-
watchers = /* @__PURE__ */ new Map();
|
|
1837
|
-
async get(key) {
|
|
1838
|
-
return this.store.get(key);
|
|
1839
|
-
}
|
|
1840
|
-
async set(key, value) {
|
|
1841
|
-
this.store.set(key, value);
|
|
1842
|
-
const keyWatchers = this.watchers.get(key);
|
|
1843
|
-
if (keyWatchers) {
|
|
1844
|
-
for (const callback of keyWatchers) {
|
|
1845
|
-
callback(value);
|
|
1846
|
-
}
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
watch(key, callback) {
|
|
1850
|
-
if (!this.watchers.has(key)) {
|
|
1851
|
-
this.watchers.set(key, /* @__PURE__ */ new Set());
|
|
1852
|
-
}
|
|
1853
|
-
const watchers = this.watchers.get(key);
|
|
1854
|
-
const wrappedCallback = (val) => callback(val);
|
|
1855
|
-
watchers.add(wrappedCallback);
|
|
1856
|
-
return () => {
|
|
1857
|
-
watchers.delete(wrappedCallback);
|
|
1858
|
-
if (watchers.size === 0) {
|
|
1859
|
-
this.watchers.delete(key);
|
|
1860
|
-
}
|
|
1861
|
-
};
|
|
1862
|
-
}
|
|
1863
|
-
/**
|
|
1864
|
-
* Get all keys (for testing/debugging)
|
|
1865
|
-
*/
|
|
1866
|
-
keys() {
|
|
1867
|
-
return Array.from(this.store.keys());
|
|
1868
|
-
}
|
|
1869
|
-
/**
|
|
1870
|
-
* Clear all data (for testing)
|
|
1871
|
-
*/
|
|
1872
|
-
clear() {
|
|
1873
|
-
this.store.clear();
|
|
1874
|
-
this.watchers.clear();
|
|
1875
|
-
}
|
|
1876
|
-
};
|
|
1877
|
-
function createInMemoryDB() {
|
|
1878
|
-
return new InMemoryPraxisDB();
|
|
1879
|
-
}
|
|
1880
|
-
var PluresDBPraxisAdapter = class {
|
|
1881
|
-
db;
|
|
1882
|
-
watchers = /* @__PURE__ */ new Map();
|
|
1883
|
-
pollIntervals = /* @__PURE__ */ new Map();
|
|
1884
|
-
lastValues = /* @__PURE__ */ new Map();
|
|
1885
|
-
pollInterval;
|
|
1886
|
-
constructor(config) {
|
|
1887
|
-
if ("get" in config && "put" in config) {
|
|
1888
|
-
this.db = config;
|
|
1889
|
-
this.pollInterval = 1e3;
|
|
1890
|
-
} else {
|
|
1891
|
-
this.db = config.db;
|
|
1892
|
-
this.pollInterval = config.pollInterval ?? 1e3;
|
|
1893
|
-
}
|
|
1894
|
-
}
|
|
1895
|
-
async get(key) {
|
|
1896
|
-
try {
|
|
1897
|
-
const value = await this.db.get(key);
|
|
1898
|
-
return value;
|
|
1899
|
-
} catch (error) {
|
|
1900
|
-
return void 0;
|
|
1901
|
-
}
|
|
1902
|
-
}
|
|
1903
|
-
async set(key, value) {
|
|
1904
|
-
await this.db.put(key, value);
|
|
1905
|
-
this.lastValues.set(key, value);
|
|
1906
|
-
const keyWatchers = this.watchers.get(key);
|
|
1907
|
-
if (keyWatchers) {
|
|
1908
|
-
for (const callback of keyWatchers) {
|
|
1909
|
-
callback(value);
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
watch(key, callback) {
|
|
1914
|
-
if (!this.watchers.has(key)) {
|
|
1915
|
-
this.watchers.set(key, /* @__PURE__ */ new Set());
|
|
1916
|
-
}
|
|
1917
|
-
const watchers = this.watchers.get(key);
|
|
1918
|
-
const wrappedCallback = (val) => callback(val);
|
|
1919
|
-
watchers.add(wrappedCallback);
|
|
1920
|
-
if (!this.pollIntervals.has(key)) {
|
|
1921
|
-
const interval = setInterval(async () => {
|
|
1922
|
-
try {
|
|
1923
|
-
const value = await this.db.get(key);
|
|
1924
|
-
const lastValue = this.lastValues.get(key);
|
|
1925
|
-
if (JSON.stringify(value) !== JSON.stringify(lastValue)) {
|
|
1926
|
-
this.lastValues.set(key, value);
|
|
1927
|
-
const currentWatchers = this.watchers.get(key);
|
|
1928
|
-
if (currentWatchers) {
|
|
1929
|
-
for (const cb of currentWatchers) {
|
|
1930
|
-
cb(value);
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
|
-
} catch (error) {
|
|
1935
|
-
}
|
|
1936
|
-
}, this.pollInterval);
|
|
1937
|
-
this.pollIntervals.set(key, interval);
|
|
1938
|
-
}
|
|
1939
|
-
return () => {
|
|
1940
|
-
watchers.delete(wrappedCallback);
|
|
1941
|
-
if (watchers.size === 0) {
|
|
1942
|
-
this.watchers.delete(key);
|
|
1943
|
-
const interval = this.pollIntervals.get(key);
|
|
1944
|
-
if (interval) {
|
|
1945
|
-
clearInterval(interval);
|
|
1946
|
-
this.pollIntervals.delete(key);
|
|
1947
|
-
}
|
|
1948
|
-
this.lastValues.delete(key);
|
|
1949
|
-
}
|
|
1950
|
-
};
|
|
1951
|
-
}
|
|
1952
|
-
/**
|
|
1953
|
-
* Clean up all resources
|
|
1954
|
-
*/
|
|
1955
|
-
dispose() {
|
|
1956
|
-
for (const interval of this.pollIntervals.values()) {
|
|
1957
|
-
clearInterval(interval);
|
|
1958
|
-
}
|
|
1959
|
-
this.pollIntervals.clear();
|
|
1960
|
-
this.watchers.clear();
|
|
1961
|
-
this.lastValues.clear();
|
|
1962
|
-
}
|
|
1963
|
-
};
|
|
1964
|
-
function createPluresDB(config) {
|
|
1965
|
-
return new PluresDBPraxisAdapter(config);
|
|
1966
|
-
}
|
|
2574
|
+
// src/integrations/pluresdb.ts
|
|
2575
|
+
init_adapter();
|
|
1967
2576
|
|
|
1968
2577
|
// src/core/pluresdb/schema-registry.ts
|
|
1969
2578
|
function getSchemaPath(schemaName) {
|
|
@@ -1980,13 +2589,13 @@ var PraxisSchemaRegistry = class {
|
|
|
1980
2589
|
* @param schema The schema to register
|
|
1981
2590
|
*/
|
|
1982
2591
|
async register(schema) {
|
|
1983
|
-
const
|
|
2592
|
+
const path3 = getSchemaPath(schema.name);
|
|
1984
2593
|
const storedSchema = {
|
|
1985
2594
|
schema,
|
|
1986
2595
|
registeredAt: Date.now(),
|
|
1987
2596
|
version: schema.version
|
|
1988
2597
|
};
|
|
1989
|
-
await this.db.set(
|
|
2598
|
+
await this.db.set(path3, storedSchema);
|
|
1990
2599
|
}
|
|
1991
2600
|
/**
|
|
1992
2601
|
* Get a schema by name
|
|
@@ -1995,8 +2604,8 @@ var PraxisSchemaRegistry = class {
|
|
|
1995
2604
|
* @returns The stored schema or undefined if not found
|
|
1996
2605
|
*/
|
|
1997
2606
|
async get(schemaName) {
|
|
1998
|
-
const
|
|
1999
|
-
return this.db.get(
|
|
2607
|
+
const path3 = getSchemaPath(schemaName);
|
|
2608
|
+
return this.db.get(path3);
|
|
2000
2609
|
}
|
|
2001
2610
|
/**
|
|
2002
2611
|
* Check if a schema is registered
|
|
@@ -3304,29 +3913,29 @@ function createMockTauriBridge() {
|
|
|
3304
3913
|
tauriVersion: "mock"
|
|
3305
3914
|
},
|
|
3306
3915
|
fs: {
|
|
3307
|
-
async readFile(
|
|
3308
|
-
const data = storage.get(
|
|
3916
|
+
async readFile(path3) {
|
|
3917
|
+
const data = storage.get(path3);
|
|
3309
3918
|
if (data instanceof Uint8Array) return data;
|
|
3310
|
-
throw new Error(`File not found: ${
|
|
3919
|
+
throw new Error(`File not found: ${path3}`);
|
|
3311
3920
|
},
|
|
3312
|
-
async readTextFile(
|
|
3313
|
-
const data = storage.get(
|
|
3921
|
+
async readTextFile(path3) {
|
|
3922
|
+
const data = storage.get(path3);
|
|
3314
3923
|
if (typeof data === "string") return data;
|
|
3315
|
-
throw new Error(`File not found: ${
|
|
3924
|
+
throw new Error(`File not found: ${path3}`);
|
|
3316
3925
|
},
|
|
3317
|
-
async writeFile(
|
|
3318
|
-
storage.set(
|
|
3926
|
+
async writeFile(path3, data) {
|
|
3927
|
+
storage.set(path3, data);
|
|
3319
3928
|
},
|
|
3320
|
-
async writeTextFile(
|
|
3321
|
-
storage.set(
|
|
3929
|
+
async writeTextFile(path3, data) {
|
|
3930
|
+
storage.set(path3, data);
|
|
3322
3931
|
},
|
|
3323
|
-
async exists(
|
|
3324
|
-
return storage.has(
|
|
3932
|
+
async exists(path3) {
|
|
3933
|
+
return storage.has(path3);
|
|
3325
3934
|
},
|
|
3326
3935
|
async mkdir(_path, _options) {
|
|
3327
3936
|
},
|
|
3328
|
-
async remove(
|
|
3329
|
-
storage.delete(
|
|
3937
|
+
async remove(path3, _options) {
|
|
3938
|
+
storage.delete(path3);
|
|
3330
3939
|
},
|
|
3331
3940
|
async rename(oldPath, newPath) {
|
|
3332
3941
|
const data = storage.get(oldPath);
|
|
@@ -3512,9 +4121,148 @@ function generateTauriConfig(config) {
|
|
|
3512
4121
|
plugins: Object.fromEntries((config.plugins || []).map((p) => [p.name, p.config || {}]))
|
|
3513
4122
|
};
|
|
3514
4123
|
}
|
|
4124
|
+
|
|
4125
|
+
// src/integrations/unified.ts
|
|
4126
|
+
async function createUnifiedApp(config) {
|
|
4127
|
+
const { createPraxisEngine: createPraxisEngine2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
4128
|
+
const { createInMemoryDB: createInMemoryDB2 } = await Promise.resolve().then(() => (init_adapter(), adapter_exports));
|
|
4129
|
+
const db = config.db || createInMemoryDB2();
|
|
4130
|
+
const pluresdb = createPluresDBAdapter({
|
|
4131
|
+
db,
|
|
4132
|
+
registry: config.registry,
|
|
4133
|
+
initialContext: config.initialContext
|
|
4134
|
+
});
|
|
4135
|
+
const engine = createPraxisEngine2({
|
|
4136
|
+
initialContext: config.initialContext,
|
|
4137
|
+
registry: config.registry
|
|
4138
|
+
});
|
|
4139
|
+
pluresdb.attachEngine(engine);
|
|
4140
|
+
const disposers = [];
|
|
4141
|
+
let unum;
|
|
4142
|
+
let channel;
|
|
4143
|
+
if (config.enableUnum) {
|
|
4144
|
+
const fullIdentity = config.unumIdentity ? {
|
|
4145
|
+
...config.unumIdentity,
|
|
4146
|
+
id: generateId(),
|
|
4147
|
+
createdAt: Date.now()
|
|
4148
|
+
} : void 0;
|
|
4149
|
+
unum = await createUnumAdapter({
|
|
4150
|
+
db,
|
|
4151
|
+
identity: fullIdentity,
|
|
4152
|
+
realtime: true
|
|
4153
|
+
});
|
|
4154
|
+
channel = await unum.createChannel(
|
|
4155
|
+
config.unumIdentity?.name || "praxis-app",
|
|
4156
|
+
[]
|
|
4157
|
+
);
|
|
4158
|
+
const unumDisposer = attachUnumToEngine(engine, unum, channel.id);
|
|
4159
|
+
disposers.push(unumDisposer);
|
|
4160
|
+
}
|
|
4161
|
+
let docs;
|
|
4162
|
+
let generateDocs2;
|
|
4163
|
+
if (config.enableDocs && config.docsConfig) {
|
|
4164
|
+
docs = createStateDocsGenerator({
|
|
4165
|
+
projectTitle: config.docsConfig.projectTitle,
|
|
4166
|
+
target: config.docsConfig.target || "./docs"
|
|
4167
|
+
});
|
|
4168
|
+
generateDocs2 = () => {
|
|
4169
|
+
const module2 = {
|
|
4170
|
+
rules: config.registry.getAllRules(),
|
|
4171
|
+
constraints: config.registry.getAllConstraints()
|
|
4172
|
+
};
|
|
4173
|
+
return docs.generateFromModule(module2);
|
|
4174
|
+
};
|
|
4175
|
+
}
|
|
4176
|
+
let canvas;
|
|
4177
|
+
if (config.schema) {
|
|
4178
|
+
canvas = schemaToCanvas(config.schema, { layout: "hierarchical" });
|
|
4179
|
+
}
|
|
4180
|
+
return {
|
|
4181
|
+
engine,
|
|
4182
|
+
pluresdb,
|
|
4183
|
+
unum,
|
|
4184
|
+
channel,
|
|
4185
|
+
docs,
|
|
4186
|
+
canvas,
|
|
4187
|
+
generateDocs: generateDocs2,
|
|
4188
|
+
dispose: () => {
|
|
4189
|
+
pluresdb.dispose();
|
|
4190
|
+
if (unum) {
|
|
4191
|
+
unum.disconnect().catch((err) => {
|
|
4192
|
+
console.warn("Warning: Error during Unum disconnect:", err);
|
|
4193
|
+
});
|
|
4194
|
+
}
|
|
4195
|
+
for (const disposer of disposers) {
|
|
4196
|
+
disposer();
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
};
|
|
4200
|
+
}
|
|
4201
|
+
async function attachAllIntegrations(engine, registry, options = {}) {
|
|
4202
|
+
const { createInMemoryDB: createInMemoryDB2 } = await Promise.resolve().then(() => (init_adapter(), adapter_exports));
|
|
4203
|
+
const db = options.db || createInMemoryDB2();
|
|
4204
|
+
const pluresdb = createPluresDBAdapter({
|
|
4205
|
+
db,
|
|
4206
|
+
registry,
|
|
4207
|
+
initialContext: engine.getContext()
|
|
4208
|
+
});
|
|
4209
|
+
pluresdb.attachEngine(engine);
|
|
4210
|
+
const disposers = [];
|
|
4211
|
+
let unum;
|
|
4212
|
+
let channel;
|
|
4213
|
+
if (options.enableUnum) {
|
|
4214
|
+
const fullIdentity = options.unumIdentity ? {
|
|
4215
|
+
...options.unumIdentity,
|
|
4216
|
+
id: generateId(),
|
|
4217
|
+
createdAt: Date.now()
|
|
4218
|
+
} : void 0;
|
|
4219
|
+
unum = await createUnumAdapter({
|
|
4220
|
+
db,
|
|
4221
|
+
identity: fullIdentity,
|
|
4222
|
+
realtime: true
|
|
4223
|
+
});
|
|
4224
|
+
channel = await unum.createChannel(
|
|
4225
|
+
options.unumIdentity?.name || "praxis-app",
|
|
4226
|
+
[]
|
|
4227
|
+
);
|
|
4228
|
+
const unumDisposer = attachUnumToEngine(engine, unum, channel.id);
|
|
4229
|
+
disposers.push(unumDisposer);
|
|
4230
|
+
}
|
|
4231
|
+
let docs;
|
|
4232
|
+
if (options.enableDocs && options.docsConfig) {
|
|
4233
|
+
docs = createStateDocsGenerator({
|
|
4234
|
+
projectTitle: options.docsConfig.projectTitle,
|
|
4235
|
+
target: options.docsConfig.target || "./docs"
|
|
4236
|
+
});
|
|
4237
|
+
}
|
|
4238
|
+
return {
|
|
4239
|
+
pluresdb,
|
|
4240
|
+
unum,
|
|
4241
|
+
channel,
|
|
4242
|
+
docs,
|
|
4243
|
+
dispose: () => {
|
|
4244
|
+
pluresdb.dispose();
|
|
4245
|
+
if (unum) {
|
|
4246
|
+
unum.disconnect().catch((err) => {
|
|
4247
|
+
console.warn("Warning: Error during Unum disconnect:", err);
|
|
4248
|
+
});
|
|
4249
|
+
}
|
|
4250
|
+
for (const disposer of disposers) {
|
|
4251
|
+
disposer();
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
};
|
|
4255
|
+
}
|
|
3515
4256
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3516
4257
|
0 && (module.exports = {
|
|
4258
|
+
AcknowledgeContractGap,
|
|
3517
4259
|
ActorManager,
|
|
4260
|
+
BehaviorLedger,
|
|
4261
|
+
ContractAdded,
|
|
4262
|
+
ContractGapAcknowledged,
|
|
4263
|
+
ContractMissing,
|
|
4264
|
+
ContractUpdated,
|
|
4265
|
+
ContractValidated,
|
|
3518
4266
|
FrameworkAgnosticReactiveEngine,
|
|
3519
4267
|
InMemoryPraxisDB,
|
|
3520
4268
|
LogicEngine,
|
|
@@ -3529,12 +4277,15 @@ function generateTauriConfig(config) {
|
|
|
3529
4277
|
RegistryIntrospector,
|
|
3530
4278
|
StateDocsGenerator,
|
|
3531
4279
|
TerminalAdapter,
|
|
4280
|
+
ValidateContracts,
|
|
4281
|
+
attachAllIntegrations,
|
|
3532
4282
|
attachTauriToEngine,
|
|
3533
4283
|
attachToEngine,
|
|
3534
4284
|
attachUnumToEngine,
|
|
3535
4285
|
canvasToMermaid,
|
|
3536
4286
|
canvasToSchema,
|
|
3537
4287
|
canvasToYaml,
|
|
4288
|
+
createBehaviorLedger,
|
|
3538
4289
|
createCanvasEditor,
|
|
3539
4290
|
createFrameworkAgnosticReactiveEngine,
|
|
3540
4291
|
createInMemoryDB,
|
|
@@ -3546,6 +4297,7 @@ function generateTauriConfig(config) {
|
|
|
3546
4297
|
createPluresDBGenerator,
|
|
3547
4298
|
createPraxisDBStore,
|
|
3548
4299
|
createPraxisEngine,
|
|
4300
|
+
createPraxisLocalFirst,
|
|
3549
4301
|
createReactiveEngine,
|
|
3550
4302
|
createSchemaRegistry,
|
|
3551
4303
|
createSchemaTemplate,
|
|
@@ -3553,8 +4305,10 @@ function generateTauriConfig(config) {
|
|
|
3553
4305
|
createTauriPraxisAdapter,
|
|
3554
4306
|
createTerminalAdapter,
|
|
3555
4307
|
createTimerActor,
|
|
4308
|
+
createUnifiedApp,
|
|
3556
4309
|
createUnumAdapter,
|
|
3557
4310
|
defineConstraint,
|
|
4311
|
+
defineContract,
|
|
3558
4312
|
defineEvent,
|
|
3559
4313
|
defineFact,
|
|
3560
4314
|
defineModule,
|
|
@@ -3563,12 +4317,17 @@ function generateTauriConfig(config) {
|
|
|
3563
4317
|
filterFacts,
|
|
3564
4318
|
findEvent,
|
|
3565
4319
|
findFact,
|
|
4320
|
+
formatValidationReport,
|
|
4321
|
+
formatValidationReportJSON,
|
|
4322
|
+
formatValidationReportSARIF,
|
|
3566
4323
|
generateDocs,
|
|
3567
4324
|
generateId,
|
|
3568
4325
|
generateTauriConfig,
|
|
4326
|
+
getContract,
|
|
3569
4327
|
getEventPath,
|
|
3570
4328
|
getFactPath,
|
|
3571
4329
|
getSchemaPath,
|
|
4330
|
+
isContract,
|
|
3572
4331
|
loadSchema,
|
|
3573
4332
|
loadSchemaFromFile,
|
|
3574
4333
|
loadSchemaFromJson,
|
|
@@ -3576,6 +4335,7 @@ function generateTauriConfig(config) {
|
|
|
3576
4335
|
registerSchema,
|
|
3577
4336
|
runTerminalCommand,
|
|
3578
4337
|
schemaToCanvas,
|
|
4338
|
+
validateContracts,
|
|
3579
4339
|
validateForGeneration,
|
|
3580
4340
|
validateSchema,
|
|
3581
4341
|
validateWithGuardian
|