@plures/praxis 1.1.2 → 1.2.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.
Files changed (49) hide show
  1. package/FRAMEWORK.md +106 -15
  2. package/README.md +275 -53
  3. package/dist/browser/adapter-TM4IS5KT.js +12 -0
  4. package/dist/browser/chunk-JQ64KMLN.js +141 -0
  5. package/dist/browser/chunk-LE2ZJYFC.js +154 -0
  6. package/dist/browser/chunk-VOMLVI6V.js +197 -0
  7. package/dist/browser/engine-YJZV4SLD.js +8 -0
  8. package/dist/browser/index.d.ts +300 -11
  9. package/dist/browser/index.js +334 -325
  10. package/dist/browser/integrations/svelte.d.ts +3 -1
  11. package/dist/browser/integrations/svelte.js +8 -0
  12. package/dist/browser/{engine-BjdqxeXG.d.ts → reactive-engine.svelte-C9OpcTHf.d.ts} +87 -1
  13. package/dist/node/adapter-K6DOX6XS.js +13 -0
  14. package/dist/node/chunk-JQ64KMLN.js +141 -0
  15. package/dist/node/chunk-LE2ZJYFC.js +154 -0
  16. package/dist/node/chunk-S54337I5.js +446 -0
  17. package/dist/node/chunk-VOMLVI6V.js +197 -0
  18. package/dist/node/cli/index.cjs +1444 -889
  19. package/dist/node/cli/index.js +9 -0
  20. package/dist/node/components/index.d.cts +2 -2
  21. package/dist/node/components/index.d.ts +2 -2
  22. package/dist/node/docs-JFNYTOJA.js +102 -0
  23. package/dist/node/engine-2DQBKBJC.js +9 -0
  24. package/dist/node/index.cjs +747 -234
  25. package/dist/node/index.d.cts +237 -15
  26. package/dist/node/index.d.ts +237 -15
  27. package/dist/node/index.js +339 -767
  28. package/dist/node/integrations/svelte.cjs +357 -2
  29. package/dist/node/integrations/svelte.d.cts +3 -1
  30. package/dist/node/integrations/svelte.d.ts +3 -1
  31. package/dist/node/integrations/svelte.js +7 -0
  32. package/dist/node/{engine-CVJobhHm.d.cts → reactive-engine.svelte-1M4m_C_v.d.cts} +87 -1
  33. package/dist/node/{engine-1iqLe6_P.d.ts → reactive-engine.svelte-ChNFn4Hj.d.ts} +87 -1
  34. package/dist/node/{terminal-adapter-XLtCjjb_.d.cts → terminal-adapter-CDzxoLKR.d.cts} +68 -1
  35. package/dist/node/{terminal-adapter-07HGftGQ.d.ts → terminal-adapter-CWka-yL8.d.ts} +68 -1
  36. package/package.json +3 -2
  37. package/src/__tests__/reactive-engine.test.ts +516 -0
  38. package/src/cli/commands/docs.ts +147 -0
  39. package/src/cli/index.ts +21 -0
  40. package/src/core/pluresdb/README.md +156 -0
  41. package/src/core/pluresdb/adapter.ts +165 -0
  42. package/src/core/pluresdb/index.ts +3 -3
  43. package/src/core/reactive-engine.svelte.ts +93 -19
  44. package/src/core/reactive-engine.ts +284 -22
  45. package/src/index.browser.ts +16 -0
  46. package/src/index.ts +16 -0
  47. package/src/integrations/pluresdb.ts +2 -2
  48. package/src/integrations/svelte.ts +8 -0
  49. package/src/integrations/unified.ts +350 -0
@@ -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,15 +30,375 @@ 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
+ });
251
+ function createInMemoryDB() {
252
+ return new InMemoryPraxisDB();
253
+ }
254
+ function createPluresDB(config) {
255
+ return new PluresDBPraxisAdapter(config);
256
+ }
257
+ var InMemoryPraxisDB, PluresDBPraxisAdapter;
258
+ var init_adapter = __esm({
259
+ "src/core/pluresdb/adapter.ts"() {
260
+ "use strict";
261
+ InMemoryPraxisDB = class {
262
+ store = /* @__PURE__ */ new Map();
263
+ watchers = /* @__PURE__ */ new Map();
264
+ async get(key) {
265
+ return this.store.get(key);
266
+ }
267
+ async set(key, value) {
268
+ this.store.set(key, value);
269
+ const keyWatchers = this.watchers.get(key);
270
+ if (keyWatchers) {
271
+ for (const callback of keyWatchers) {
272
+ callback(value);
273
+ }
274
+ }
275
+ }
276
+ watch(key, callback) {
277
+ if (!this.watchers.has(key)) {
278
+ this.watchers.set(key, /* @__PURE__ */ new Set());
279
+ }
280
+ const watchers = this.watchers.get(key);
281
+ const wrappedCallback = (val) => callback(val);
282
+ watchers.add(wrappedCallback);
283
+ return () => {
284
+ watchers.delete(wrappedCallback);
285
+ if (watchers.size === 0) {
286
+ this.watchers.delete(key);
287
+ }
288
+ };
289
+ }
290
+ /**
291
+ * Get all keys (for testing/debugging)
292
+ */
293
+ keys() {
294
+ return Array.from(this.store.keys());
295
+ }
296
+ /**
297
+ * Clear all data (for testing)
298
+ */
299
+ clear() {
300
+ this.store.clear();
301
+ this.watchers.clear();
302
+ }
303
+ };
304
+ PluresDBPraxisAdapter = class {
305
+ db;
306
+ watchers = /* @__PURE__ */ new Map();
307
+ pollIntervals = /* @__PURE__ */ new Map();
308
+ lastValues = /* @__PURE__ */ new Map();
309
+ pollInterval;
310
+ constructor(config) {
311
+ if ("get" in config && "put" in config) {
312
+ this.db = config;
313
+ this.pollInterval = 1e3;
314
+ } else {
315
+ this.db = config.db;
316
+ this.pollInterval = config.pollInterval ?? 1e3;
317
+ }
318
+ }
319
+ async get(key) {
320
+ try {
321
+ const value = await this.db.get(key);
322
+ return value;
323
+ } catch (error) {
324
+ return void 0;
325
+ }
326
+ }
327
+ async set(key, value) {
328
+ await this.db.put(key, value);
329
+ this.lastValues.set(key, value);
330
+ const keyWatchers = this.watchers.get(key);
331
+ if (keyWatchers) {
332
+ for (const callback of keyWatchers) {
333
+ callback(value);
334
+ }
335
+ }
336
+ }
337
+ watch(key, callback) {
338
+ if (!this.watchers.has(key)) {
339
+ this.watchers.set(key, /* @__PURE__ */ new Set());
340
+ }
341
+ const watchers = this.watchers.get(key);
342
+ const wrappedCallback = (val) => callback(val);
343
+ watchers.add(wrappedCallback);
344
+ if (!this.pollIntervals.has(key)) {
345
+ const interval = setInterval(async () => {
346
+ try {
347
+ const value = await this.db.get(key);
348
+ const lastValue = this.lastValues.get(key);
349
+ if (JSON.stringify(value) !== JSON.stringify(lastValue)) {
350
+ this.lastValues.set(key, value);
351
+ const currentWatchers = this.watchers.get(key);
352
+ if (currentWatchers) {
353
+ for (const cb of currentWatchers) {
354
+ cb(value);
355
+ }
356
+ }
357
+ }
358
+ } catch (error) {
359
+ }
360
+ }, this.pollInterval);
361
+ this.pollIntervals.set(key, interval);
362
+ }
363
+ return () => {
364
+ watchers.delete(wrappedCallback);
365
+ if (watchers.size === 0) {
366
+ this.watchers.delete(key);
367
+ const interval = this.pollIntervals.get(key);
368
+ if (interval) {
369
+ clearInterval(interval);
370
+ this.pollIntervals.delete(key);
371
+ }
372
+ this.lastValues.delete(key);
373
+ }
374
+ };
375
+ }
376
+ /**
377
+ * Clean up all resources
378
+ */
379
+ dispose() {
380
+ for (const interval of this.pollIntervals.values()) {
381
+ clearInterval(interval);
382
+ }
383
+ this.pollIntervals.clear();
384
+ this.watchers.clear();
385
+ this.lastValues.clear();
386
+ }
387
+ };
388
+ }
389
+ });
390
+
30
391
  // src/index.ts
31
392
  var src_exports = {};
32
393
  __export(src_exports, {
33
394
  ActorManager: () => ActorManager,
395
+ FrameworkAgnosticReactiveEngine: () => ReactiveLogicEngine2,
34
396
  InMemoryPraxisDB: () => InMemoryPraxisDB,
35
397
  LogicEngine: () => LogicEngine,
36
398
  PRAXIS_PATHS: () => PRAXIS_PATHS,
37
399
  PRAXIS_PROTOCOL_VERSION: () => PRAXIS_PROTOCOL_VERSION,
38
400
  PluresDBGenerator: () => PluresDBGenerator,
401
+ PluresDBPraxisAdapter: () => PluresDBPraxisAdapter,
39
402
  PraxisDBStore: () => PraxisDBStore,
40
403
  PraxisRegistry: () => PraxisRegistry,
41
404
  PraxisSchemaRegistry: () => PraxisSchemaRegistry,
@@ -43,6 +406,7 @@ __export(src_exports, {
43
406
  RegistryIntrospector: () => RegistryIntrospector,
44
407
  StateDocsGenerator: () => StateDocsGenerator,
45
408
  TerminalAdapter: () => TerminalAdapter,
409
+ attachAllIntegrations: () => attachAllIntegrations,
46
410
  attachTauriToEngine: () => attachTauriToEngine,
47
411
  attachToEngine: () => attachToEngine,
48
412
  attachUnumToEngine: () => attachUnumToEngine,
@@ -50,20 +414,24 @@ __export(src_exports, {
50
414
  canvasToSchema: () => canvasToSchema,
51
415
  canvasToYaml: () => canvasToYaml,
52
416
  createCanvasEditor: () => createCanvasEditor,
417
+ createFrameworkAgnosticReactiveEngine: () => createReactiveEngine2,
53
418
  createInMemoryDB: () => createInMemoryDB,
54
419
  createIntrospector: () => createIntrospector,
55
420
  createMockExecutor: () => createMockExecutor,
56
421
  createMockTauriBridge: () => createMockTauriBridge,
422
+ createPluresDB: () => createPluresDB,
57
423
  createPluresDBAdapter: () => createPluresDBAdapter,
58
424
  createPluresDBGenerator: () => createPluresDBGenerator,
59
425
  createPraxisDBStore: () => createPraxisDBStore,
60
426
  createPraxisEngine: () => createPraxisEngine,
427
+ createReactiveEngine: () => createReactiveEngine,
61
428
  createSchemaRegistry: () => createSchemaRegistry,
62
429
  createSchemaTemplate: () => createSchemaTemplate,
63
430
  createStateDocsGenerator: () => createStateDocsGenerator,
64
431
  createTauriPraxisAdapter: () => createTauriPraxisAdapter,
65
432
  createTerminalAdapter: () => createTerminalAdapter,
66
433
  createTimerActor: () => createTimerActor,
434
+ createUnifiedApp: () => createUnifiedApp,
67
435
  createUnumAdapter: () => createUnumAdapter,
68
436
  defineConstraint: () => defineConstraint,
69
437
  defineEvent: () => defineEvent,
@@ -92,9 +460,7 @@ __export(src_exports, {
92
460
  validateWithGuardian: () => validateWithGuardian
93
461
  });
94
462
  module.exports = __toCommonJS(src_exports);
95
-
96
- // src/core/protocol.ts
97
- var PRAXIS_PROTOCOL_VERSION = "1.0.0";
463
+ init_protocol();
98
464
 
99
465
  // src/core/rules.ts
100
466
  var PraxisRegistry = class {
@@ -167,244 +533,297 @@ var PraxisRegistry = class {
167
533
  }
168
534
  };
169
535
 
170
- // src/core/engine.ts
171
- function safeClone(value) {
172
- if (value === null || typeof value !== "object") {
173
- return value;
174
- }
175
- if (typeof globalThis.structuredClone === "function") {
176
- try {
177
- return globalThis.structuredClone(value);
178
- } catch {
179
- }
536
+ // src/index.ts
537
+ init_engine();
538
+
539
+ // src/core/reactive-engine.svelte.ts
540
+ var $ = __toESM(require("svelte/internal/client"), 1);
541
+ init_engine();
542
+ var ReactiveLogicEngine = class {
543
+ #state = (
544
+ // Use Svelte's $state rune for automatic reactivity
545
+ $.state($.proxy({ context: {}, facts: [], meta: {} }))
546
+ );
547
+ get state() {
548
+ return $.get(this.#state);
180
549
  }
181
- if (Array.isArray(value)) {
182
- return [...value];
550
+ set state(value) {
551
+ $.set(this.#state, value, true);
183
552
  }
184
- return { ...value };
185
- }
186
- var LogicEngine = class {
187
- state;
188
- registry;
553
+ _engine;
189
554
  constructor(options) {
190
- this.registry = options.registry;
191
- this.state = {
192
- context: options.initialContext,
193
- facts: options.initialFacts ?? [],
194
- meta: options.initialMeta ?? {},
195
- protocolVersion: PRAXIS_PROTOCOL_VERSION
196
- };
555
+ this.state.context = options.initialContext;
556
+ this.state.facts = options.initialFacts ?? [];
557
+ this.state.meta = options.initialMeta ?? {};
558
+ if (options.registry) {
559
+ this._engine = createPraxisEngine({
560
+ initialContext: options.initialContext,
561
+ registry: options.registry
562
+ });
563
+ } else {
564
+ this._engine = createPraxisEngine({
565
+ initialContext: options.initialContext,
566
+ registry: new PraxisRegistry()
567
+ });
568
+ }
197
569
  }
198
570
  /**
199
- * Get the current state (immutable copy)
571
+ * Access the reactive context.
572
+ * In Svelte 5 components, changes to this object will automatically trigger updates.
200
573
  */
201
- getState() {
202
- return {
203
- context: safeClone(this.state.context),
204
- facts: [...this.state.facts],
205
- meta: this.state.meta ? safeClone(this.state.meta) : void 0,
206
- protocolVersion: this.state.protocolVersion
207
- };
574
+ get context() {
575
+ return this.state.context;
208
576
  }
209
577
  /**
210
- * Get the current context
578
+ * Access the reactive facts list.
211
579
  */
212
- getContext() {
213
- return safeClone(this.state.context);
580
+ get facts() {
581
+ return this.state.facts;
214
582
  }
215
583
  /**
216
- * Get current facts
584
+ * Access the reactive metadata.
217
585
  */
218
- getFacts() {
219
- return [...this.state.facts];
586
+ get meta() {
587
+ return this.state.meta;
220
588
  }
221
589
  /**
222
- * Process events through the engine.
223
- * Applies all registered rules and checks all registered constraints.
224
- *
225
- * @param events Events to process
226
- * @returns Result with new state and diagnostics
590
+ * Apply a mutation to the state.
591
+ * Changes will automatically trigger Svelte reactivity.
592
+ *
593
+ * @param mutator A function that receives the state and modifies it.
227
594
  */
228
- step(events) {
229
- const config = {
230
- ruleIds: this.registry.getRuleIds(),
231
- constraintIds: this.registry.getConstraintIds()
232
- };
233
- return this.stepWithConfig(events, config);
595
+ apply(mutator) {
596
+ mutator(this.state);
234
597
  }
235
598
  /**
236
- * Process events with specific rule and constraint configuration.
237
- *
599
+ * Process events through the logic engine and update reactive state.
600
+ *
238
601
  * @param events Events to process
239
- * @param config Step configuration
240
- * @returns Result with new state and diagnostics
241
- */
242
- stepWithConfig(events, config) {
243
- const diagnostics = [];
244
- let newState = { ...this.state };
245
- const newFacts = [];
246
- for (const ruleId of config.ruleIds) {
247
- const rule = this.registry.getRule(ruleId);
248
- if (!rule) {
249
- diagnostics.push({
250
- kind: "rule-error",
251
- message: `Rule "${ruleId}" not found in registry`,
252
- data: { ruleId }
253
- });
254
- continue;
255
- }
256
- try {
257
- const ruleFacts = rule.impl(newState, events);
258
- newFacts.push(...ruleFacts);
259
- } catch (error) {
260
- diagnostics.push({
261
- kind: "rule-error",
262
- message: `Error executing rule "${ruleId}": ${error instanceof Error ? error.message : String(error)}`,
263
- data: { ruleId, error }
264
- });
265
- }
266
- }
267
- newState = {
268
- ...newState,
269
- facts: [...newState.facts, ...newFacts]
270
- };
271
- for (const constraintId of config.constraintIds) {
272
- const constraint = this.registry.getConstraint(constraintId);
273
- if (!constraint) {
274
- diagnostics.push({
275
- kind: "constraint-violation",
276
- message: `Constraint "${constraintId}" not found in registry`,
277
- data: { constraintId }
278
- });
279
- continue;
280
- }
281
- try {
282
- const result = constraint.impl(newState);
283
- if (result === false) {
284
- diagnostics.push({
285
- kind: "constraint-violation",
286
- message: `Constraint "${constraintId}" violated`,
287
- data: { constraintId, description: constraint.description }
288
- });
289
- } else if (typeof result === "string") {
290
- diagnostics.push({
291
- kind: "constraint-violation",
292
- message: result,
293
- data: { constraintId, description: constraint.description }
294
- });
295
- }
296
- } catch (error) {
297
- diagnostics.push({
298
- kind: "constraint-violation",
299
- message: `Error checking constraint "${constraintId}": ${error instanceof Error ? error.message : String(error)}`,
300
- data: { constraintId, error }
301
- });
302
- }
303
- }
304
- this.state = newState;
305
- return {
306
- state: newState,
307
- diagnostics
308
- };
309
- }
310
- /**
311
- * Update the context directly (for exceptional cases).
312
- * Generally, context should be updated through rules.
313
- *
314
- * @param updater Function that produces new context from old context
315
602
  */
316
- updateContext(updater) {
317
- this.state = {
318
- ...this.state,
319
- context: updater(this.state.context)
603
+ step(events) {
604
+ const result = this._engine.step(events);
605
+ this.state.context = result.state.context;
606
+ this.state.facts = result.state.facts;
607
+ this.state.meta = result.state.meta ?? {};
608
+ }
609
+ };
610
+ function createReactiveEngine(options) {
611
+ return new ReactiveLogicEngine(options);
612
+ }
613
+
614
+ // src/core/reactive-engine.ts
615
+ var ReactiveLogicEngine2 = class _ReactiveLogicEngine {
616
+ _state;
617
+ _subscribers = /* @__PURE__ */ new Set();
618
+ _contextProxy;
619
+ _factsProxy;
620
+ _metaProxy;
621
+ _batchDepth = 0;
622
+ _pendingNotification = false;
623
+ _proxyCache = /* @__PURE__ */ new WeakMap();
624
+ // Array methods that mutate the array
625
+ static ARRAY_MUTATORS = ["push", "pop", "shift", "unshift", "splice", "sort", "reverse"];
626
+ constructor(options) {
627
+ this._state = {
628
+ context: options.initialContext,
629
+ facts: options.initialFacts ?? [],
630
+ meta: options.initialMeta ?? {}
320
631
  };
632
+ this._contextProxy = this._createReactiveProxy(this._state.context);
633
+ this._factsProxy = this._createReactiveProxy(this._state.facts);
634
+ this._metaProxy = this._createReactiveProxy(this._state.meta);
321
635
  }
322
636
  /**
323
- * Add facts directly (for exceptional cases).
324
- * Generally, facts should be added through rules.
325
- *
326
- * @param facts Facts to add
637
+ * Create a reactive proxy that notifies subscribers on changes.
638
+ * Uses a WeakMap cache to avoid creating multiple proxies for the same object.
327
639
  */
328
- addFacts(facts) {
329
- this.state = {
330
- ...this.state,
331
- facts: [...this.state.facts, ...facts]
640
+ _createReactiveProxy(target) {
641
+ const cached = this._proxyCache.get(target);
642
+ if (cached) {
643
+ return cached;
644
+ }
645
+ const self = this;
646
+ const handler = {
647
+ get(obj, prop) {
648
+ const value = Reflect.get(obj, prop);
649
+ if (value && typeof value === "object") {
650
+ return self._createReactiveProxy(value);
651
+ }
652
+ if (Array.isArray(obj) && typeof value === "function") {
653
+ if (_ReactiveLogicEngine.ARRAY_MUTATORS.includes(prop)) {
654
+ return function(...args) {
655
+ const result = value.apply(obj, args);
656
+ self._notify();
657
+ return result;
658
+ };
659
+ }
660
+ }
661
+ return value;
662
+ },
663
+ set(obj, prop, value) {
664
+ const oldValue = obj[prop];
665
+ const result = Reflect.set(obj, prop, value);
666
+ if (oldValue !== value) {
667
+ self._notify();
668
+ }
669
+ return result;
670
+ },
671
+ deleteProperty(obj, prop) {
672
+ const result = Reflect.deleteProperty(obj, prop);
673
+ self._notify();
674
+ return result;
675
+ }
332
676
  };
677
+ const proxy2 = new Proxy(target, handler);
678
+ this._proxyCache.set(target, proxy2);
679
+ return proxy2;
333
680
  }
334
681
  /**
335
- * Clear all facts
682
+ * Notify all subscribers of state changes
336
683
  */
337
- clearFacts() {
338
- this.state = {
339
- ...this.state,
340
- facts: []
684
+ _notify() {
685
+ if (this._batchDepth > 0) {
686
+ this._pendingNotification = true;
687
+ return;
688
+ }
689
+ const currentState = {
690
+ context: this._contextProxy,
691
+ facts: this._factsProxy,
692
+ meta: this._metaProxy
341
693
  };
694
+ this._subscribers.forEach((callback) => {
695
+ try {
696
+ callback(currentState);
697
+ } catch (error) {
698
+ console.error("Error in reactive engine subscriber:", error);
699
+ }
700
+ });
342
701
  }
343
702
  /**
344
- * Reset the engine to initial state
703
+ * Get the full state object
345
704
  */
346
- reset(options) {
347
- this.state = {
348
- context: options.initialContext,
349
- facts: options.initialFacts ?? [],
350
- meta: options.initialMeta ?? {},
351
- protocolVersion: PRAXIS_PROTOCOL_VERSION
352
- };
353
- }
354
- };
355
- function createPraxisEngine(options) {
356
- return new LogicEngine(options);
357
- }
358
-
359
- // src/core/reactive-engine.svelte.ts
360
- var $ = __toESM(require("svelte/internal/client"), 1);
361
- var ReactiveLogicEngine = class {
362
- #state = (
363
- // The single source of truth, reactive by default
364
- // We use $state.raw for things that shouldn't be deeply reactive if needed,
365
- // but for context we usually want deep reactivity.
366
- $.state($.proxy({ context: {}, facts: [], meta: {} }))
367
- );
368
705
  get state() {
369
- return $.get(this.#state);
370
- }
371
- set state(value) {
372
- $.set(this.#state, value, true);
373
- }
374
- constructor(options) {
375
- this.state.context = options.initialContext;
376
- this.state.facts = options.initialFacts ?? [];
377
- this.state.meta = options.initialMeta ?? {};
706
+ return {
707
+ context: this._contextProxy,
708
+ facts: this._factsProxy,
709
+ meta: this._metaProxy
710
+ };
378
711
  }
379
712
  /**
380
- * Access the reactive context directly.
381
- * Consumers can use this in $derived() or $effect().
713
+ * Access the reactive context.
714
+ * Changes to this object will trigger subscriber notifications.
382
715
  */
383
716
  get context() {
384
- return this.state.context;
717
+ return this._contextProxy;
385
718
  }
386
719
  /**
387
720
  * Access the reactive facts list.
721
+ * Changes to this array will trigger subscriber notifications.
388
722
  */
389
723
  get facts() {
390
- return this.state.facts;
724
+ return this._factsProxy;
725
+ }
726
+ /**
727
+ * Access the reactive metadata.
728
+ * Changes to this object will trigger subscriber notifications.
729
+ */
730
+ get meta() {
731
+ return this._metaProxy;
391
732
  }
392
733
  /**
393
734
  * Apply a mutation to the state.
394
735
  * This is the "Action" or "Rule" equivalent.
736
+ * Mutations are batched - notifications only happen once per apply call.
395
737
  *
396
738
  * @param mutator A function that receives the state and modifies it.
397
739
  */
398
740
  apply(mutator) {
399
- mutator(this.state);
741
+ this._batchDepth++;
742
+ try {
743
+ mutator({
744
+ context: this._contextProxy,
745
+ facts: this._factsProxy,
746
+ meta: this._metaProxy
747
+ });
748
+ } finally {
749
+ this._batchDepth--;
750
+ if (this._batchDepth === 0 && this._pendingNotification) {
751
+ this._pendingNotification = false;
752
+ this._notify();
753
+ }
754
+ }
400
755
  }
401
756
  /**
402
- * Access the reactive meta.
757
+ * Subscribe to state changes.
758
+ * Returns an unsubscribe function.
759
+ *
760
+ * @param callback Function to call when state changes
761
+ * @returns Unsubscribe function
403
762
  */
404
- get meta() {
405
- return this.state.meta;
763
+ subscribe(callback) {
764
+ this._subscribers.add(callback);
765
+ try {
766
+ callback({
767
+ context: this._contextProxy,
768
+ facts: this._factsProxy,
769
+ meta: this._metaProxy
770
+ });
771
+ } catch (error) {
772
+ console.error("Error in reactive engine subscriber:", error);
773
+ }
774
+ return () => {
775
+ this._subscribers.delete(callback);
776
+ };
777
+ }
778
+ /**
779
+ * Create a derived/computed value from the state.
780
+ * The selector function will be called whenever the state changes.
781
+ *
782
+ * @param selector Function to extract derived value from state
783
+ * @returns Object with subscribe method for reactive updates
784
+ */
785
+ $derived(selector) {
786
+ const subscribers = /* @__PURE__ */ new Set();
787
+ let currentValue = selector({
788
+ context: this._contextProxy,
789
+ facts: this._factsProxy,
790
+ meta: this._metaProxy
791
+ });
792
+ this.subscribe(() => {
793
+ const newValue = selector({
794
+ context: this._contextProxy,
795
+ facts: this._factsProxy,
796
+ meta: this._metaProxy
797
+ });
798
+ if (newValue !== currentValue) {
799
+ currentValue = newValue;
800
+ subscribers.forEach((callback) => {
801
+ try {
802
+ callback(currentValue);
803
+ } catch (error) {
804
+ console.error("Error in derived value subscriber:", error);
805
+ }
806
+ });
807
+ }
808
+ });
809
+ return {
810
+ subscribe: (callback) => {
811
+ subscribers.add(callback);
812
+ try {
813
+ callback(currentValue);
814
+ } catch (error) {
815
+ console.error("Error in derived value subscriber:", error);
816
+ }
817
+ return () => {
818
+ subscribers.delete(callback);
819
+ };
820
+ }
821
+ };
406
822
  }
407
823
  };
824
+ function createReactiveEngine2(options) {
825
+ return new ReactiveLogicEngine2(options);
826
+ }
408
827
 
409
828
  // src/core/actors.ts
410
829
  var ActorManager = class {
@@ -1587,53 +2006,8 @@ function createPraxisDBStore(db, registry, initialContext, onRuleError) {
1587
2006
  return new PraxisDBStore({ db, registry, initialContext, onRuleError });
1588
2007
  }
1589
2008
 
1590
- // src/core/pluresdb/adapter.ts
1591
- var InMemoryPraxisDB = class {
1592
- store = /* @__PURE__ */ new Map();
1593
- watchers = /* @__PURE__ */ new Map();
1594
- async get(key) {
1595
- return this.store.get(key);
1596
- }
1597
- async set(key, value) {
1598
- this.store.set(key, value);
1599
- const keyWatchers = this.watchers.get(key);
1600
- if (keyWatchers) {
1601
- for (const callback of keyWatchers) {
1602
- callback(value);
1603
- }
1604
- }
1605
- }
1606
- watch(key, callback) {
1607
- if (!this.watchers.has(key)) {
1608
- this.watchers.set(key, /* @__PURE__ */ new Set());
1609
- }
1610
- const watchers = this.watchers.get(key);
1611
- const wrappedCallback = (val) => callback(val);
1612
- watchers.add(wrappedCallback);
1613
- return () => {
1614
- watchers.delete(wrappedCallback);
1615
- if (watchers.size === 0) {
1616
- this.watchers.delete(key);
1617
- }
1618
- };
1619
- }
1620
- /**
1621
- * Get all keys (for testing/debugging)
1622
- */
1623
- keys() {
1624
- return Array.from(this.store.keys());
1625
- }
1626
- /**
1627
- * Clear all data (for testing)
1628
- */
1629
- clear() {
1630
- this.store.clear();
1631
- this.watchers.clear();
1632
- }
1633
- };
1634
- function createInMemoryDB() {
1635
- return new InMemoryPraxisDB();
1636
- }
2009
+ // src/integrations/pluresdb.ts
2010
+ init_adapter();
1637
2011
 
1638
2012
  // src/core/pluresdb/schema-registry.ts
1639
2013
  function getSchemaPath(schemaName) {
@@ -3182,14 +3556,148 @@ function generateTauriConfig(config) {
3182
3556
  plugins: Object.fromEntries((config.plugins || []).map((p) => [p.name, p.config || {}]))
3183
3557
  };
3184
3558
  }
3559
+
3560
+ // src/integrations/unified.ts
3561
+ async function createUnifiedApp(config) {
3562
+ const { createPraxisEngine: createPraxisEngine2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
3563
+ const { createInMemoryDB: createInMemoryDB2 } = await Promise.resolve().then(() => (init_adapter(), adapter_exports));
3564
+ const db = config.db || createInMemoryDB2();
3565
+ const pluresdb = createPluresDBAdapter({
3566
+ db,
3567
+ registry: config.registry,
3568
+ initialContext: config.initialContext
3569
+ });
3570
+ const engine = createPraxisEngine2({
3571
+ initialContext: config.initialContext,
3572
+ registry: config.registry
3573
+ });
3574
+ pluresdb.attachEngine(engine);
3575
+ const disposers = [];
3576
+ let unum;
3577
+ let channel;
3578
+ if (config.enableUnum) {
3579
+ const fullIdentity = config.unumIdentity ? {
3580
+ ...config.unumIdentity,
3581
+ id: generateId(),
3582
+ createdAt: Date.now()
3583
+ } : void 0;
3584
+ unum = await createUnumAdapter({
3585
+ db,
3586
+ identity: fullIdentity,
3587
+ realtime: true
3588
+ });
3589
+ channel = await unum.createChannel(
3590
+ config.unumIdentity?.name || "praxis-app",
3591
+ []
3592
+ );
3593
+ const unumDisposer = attachUnumToEngine(engine, unum, channel.id);
3594
+ disposers.push(unumDisposer);
3595
+ }
3596
+ let docs;
3597
+ let generateDocs2;
3598
+ if (config.enableDocs && config.docsConfig) {
3599
+ docs = createStateDocsGenerator({
3600
+ projectTitle: config.docsConfig.projectTitle,
3601
+ target: config.docsConfig.target || "./docs"
3602
+ });
3603
+ generateDocs2 = () => {
3604
+ const module2 = {
3605
+ rules: config.registry.getAllRules(),
3606
+ constraints: config.registry.getAllConstraints()
3607
+ };
3608
+ return docs.generateFromModule(module2);
3609
+ };
3610
+ }
3611
+ let canvas;
3612
+ if (config.schema) {
3613
+ canvas = schemaToCanvas(config.schema, { layout: "hierarchical" });
3614
+ }
3615
+ return {
3616
+ engine,
3617
+ pluresdb,
3618
+ unum,
3619
+ channel,
3620
+ docs,
3621
+ canvas,
3622
+ generateDocs: generateDocs2,
3623
+ dispose: () => {
3624
+ pluresdb.dispose();
3625
+ if (unum) {
3626
+ unum.disconnect().catch((err) => {
3627
+ console.warn("Warning: Error during Unum disconnect:", err);
3628
+ });
3629
+ }
3630
+ for (const disposer of disposers) {
3631
+ disposer();
3632
+ }
3633
+ }
3634
+ };
3635
+ }
3636
+ async function attachAllIntegrations(engine, registry, options = {}) {
3637
+ const { createInMemoryDB: createInMemoryDB2 } = await Promise.resolve().then(() => (init_adapter(), adapter_exports));
3638
+ const db = options.db || createInMemoryDB2();
3639
+ const pluresdb = createPluresDBAdapter({
3640
+ db,
3641
+ registry,
3642
+ initialContext: engine.getContext()
3643
+ });
3644
+ pluresdb.attachEngine(engine);
3645
+ const disposers = [];
3646
+ let unum;
3647
+ let channel;
3648
+ if (options.enableUnum) {
3649
+ const fullIdentity = options.unumIdentity ? {
3650
+ ...options.unumIdentity,
3651
+ id: generateId(),
3652
+ createdAt: Date.now()
3653
+ } : void 0;
3654
+ unum = await createUnumAdapter({
3655
+ db,
3656
+ identity: fullIdentity,
3657
+ realtime: true
3658
+ });
3659
+ channel = await unum.createChannel(
3660
+ options.unumIdentity?.name || "praxis-app",
3661
+ []
3662
+ );
3663
+ const unumDisposer = attachUnumToEngine(engine, unum, channel.id);
3664
+ disposers.push(unumDisposer);
3665
+ }
3666
+ let docs;
3667
+ if (options.enableDocs && options.docsConfig) {
3668
+ docs = createStateDocsGenerator({
3669
+ projectTitle: options.docsConfig.projectTitle,
3670
+ target: options.docsConfig.target || "./docs"
3671
+ });
3672
+ }
3673
+ return {
3674
+ pluresdb,
3675
+ unum,
3676
+ channel,
3677
+ docs,
3678
+ dispose: () => {
3679
+ pluresdb.dispose();
3680
+ if (unum) {
3681
+ unum.disconnect().catch((err) => {
3682
+ console.warn("Warning: Error during Unum disconnect:", err);
3683
+ });
3684
+ }
3685
+ for (const disposer of disposers) {
3686
+ disposer();
3687
+ }
3688
+ }
3689
+ };
3690
+ }
3185
3691
  // Annotate the CommonJS export names for ESM import in node:
3186
3692
  0 && (module.exports = {
3187
3693
  ActorManager,
3694
+ FrameworkAgnosticReactiveEngine,
3188
3695
  InMemoryPraxisDB,
3189
3696
  LogicEngine,
3190
3697
  PRAXIS_PATHS,
3191
3698
  PRAXIS_PROTOCOL_VERSION,
3192
3699
  PluresDBGenerator,
3700
+ PluresDBPraxisAdapter,
3193
3701
  PraxisDBStore,
3194
3702
  PraxisRegistry,
3195
3703
  PraxisSchemaRegistry,
@@ -3197,6 +3705,7 @@ function generateTauriConfig(config) {
3197
3705
  RegistryIntrospector,
3198
3706
  StateDocsGenerator,
3199
3707
  TerminalAdapter,
3708
+ attachAllIntegrations,
3200
3709
  attachTauriToEngine,
3201
3710
  attachToEngine,
3202
3711
  attachUnumToEngine,
@@ -3204,20 +3713,24 @@ function generateTauriConfig(config) {
3204
3713
  canvasToSchema,
3205
3714
  canvasToYaml,
3206
3715
  createCanvasEditor,
3716
+ createFrameworkAgnosticReactiveEngine,
3207
3717
  createInMemoryDB,
3208
3718
  createIntrospector,
3209
3719
  createMockExecutor,
3210
3720
  createMockTauriBridge,
3721
+ createPluresDB,
3211
3722
  createPluresDBAdapter,
3212
3723
  createPluresDBGenerator,
3213
3724
  createPraxisDBStore,
3214
3725
  createPraxisEngine,
3726
+ createReactiveEngine,
3215
3727
  createSchemaRegistry,
3216
3728
  createSchemaTemplate,
3217
3729
  createStateDocsGenerator,
3218
3730
  createTauriPraxisAdapter,
3219
3731
  createTerminalAdapter,
3220
3732
  createTimerActor,
3733
+ createUnifiedApp,
3221
3734
  createUnumAdapter,
3222
3735
  defineConstraint,
3223
3736
  defineEvent,