@plures/praxis 1.1.1 → 1.1.3
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/README.md +68 -7
- package/dist/browser/chunk-R45WXWKH.js +345 -0
- package/dist/browser/index.d.ts +171 -11
- package/dist/browser/index.js +279 -277
- package/dist/browser/integrations/svelte.d.ts +3 -1
- package/dist/browser/integrations/svelte.js +7 -0
- package/dist/browser/{engine-BjdqxeXG.d.ts → reactive-engine.svelte-C9OpcTHf.d.ts} +87 -1
- package/dist/node/chunk-R45WXWKH.js +345 -0
- package/dist/node/components/index.d.cts +2 -2
- package/dist/node/components/index.d.ts +2 -2
- package/dist/node/index.cjs +343 -8
- package/dist/node/index.d.cts +108 -15
- package/dist/node/index.d.ts +108 -15
- package/dist/node/index.js +279 -278
- package/dist/node/integrations/svelte.cjs +357 -2
- package/dist/node/integrations/svelte.d.cts +3 -1
- package/dist/node/integrations/svelte.d.ts +3 -1
- package/dist/node/integrations/svelte.js +6 -0
- package/dist/node/{engine-CVJobhHm.d.cts → reactive-engine.svelte-1M4m_C_v.d.cts} +87 -1
- package/dist/node/{engine-1iqLe6_P.d.ts → reactive-engine.svelte-ChNFn4Hj.d.ts} +87 -1
- package/dist/node/{terminal-adapter-XLtCjjb_.d.cts → terminal-adapter-CDzxoLKR.d.cts} +68 -1
- package/dist/node/{terminal-adapter-07HGftGQ.d.ts → terminal-adapter-CWka-yL8.d.ts} +68 -1
- package/package.json +3 -2
- package/src/__tests__/reactive-engine.test.ts +516 -0
- package/src/core/pluresdb/README.md +156 -0
- package/src/core/pluresdb/adapter.ts +165 -0
- package/src/core/pluresdb/index.ts +3 -3
- package/src/core/reactive-engine.svelte.ts +88 -19
- package/src/core/reactive-engine.ts +283 -30
- package/src/index.browser.ts +12 -0
- package/src/index.ts +12 -0
- package/src/integrations/pluresdb.ts +2 -2
- package/src/integrations/svelte.ts +8 -0
|
@@ -1,67 +1,320 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Praxis Reactive Logic Engine
|
|
3
3
|
*
|
|
4
|
-
* A
|
|
5
|
-
* Uses
|
|
4
|
+
* A framework-agnostic reactive implementation of the Praxis Logic Engine.
|
|
5
|
+
* Uses JavaScript Proxies to provide reactivity without Svelte-specific primitives.
|
|
6
|
+
*
|
|
7
|
+
* This implementation provides:
|
|
8
|
+
* - Proxy-based state tracking for automatic reactivity
|
|
9
|
+
* - Subscription-based change notifications
|
|
10
|
+
* - Computed/derived values support
|
|
11
|
+
* - Compatible API with Svelte-based implementation
|
|
6
12
|
*/
|
|
7
13
|
|
|
8
|
-
console.log("Reactive Engine Loaded");
|
|
9
|
-
|
|
10
14
|
export interface ReactiveEngineOptions<TContext> {
|
|
11
15
|
initialContext: TContext;
|
|
12
16
|
initialFacts?: any[];
|
|
13
17
|
initialMeta?: Record<string, unknown>;
|
|
14
18
|
}
|
|
15
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Callback type for state change subscribers
|
|
22
|
+
*/
|
|
23
|
+
export type StateChangeCallback<TContext> = (state: {
|
|
24
|
+
context: TContext;
|
|
25
|
+
facts: any[];
|
|
26
|
+
meta: Record<string, unknown>;
|
|
27
|
+
}) => void;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Callback type for unsubscribe function
|
|
31
|
+
*/
|
|
32
|
+
export type UnsubscribeFn = () => void;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Framework-agnostic reactive logic engine using JavaScript Proxies
|
|
36
|
+
*/
|
|
16
37
|
export class ReactiveLogicEngine<TContext extends object> {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
});
|
|
38
|
+
private _state: { context: TContext; facts: any[]; meta: Record<string, unknown> };
|
|
39
|
+
private _subscribers = new Set<StateChangeCallback<TContext>>();
|
|
40
|
+
private _contextProxy: TContext;
|
|
41
|
+
private _factsProxy: any[];
|
|
42
|
+
private _metaProxy: Record<string, unknown>;
|
|
43
|
+
private _batchDepth = 0;
|
|
44
|
+
private _pendingNotification = false;
|
|
45
|
+
private _proxyCache = new WeakMap<object, any>();
|
|
46
|
+
|
|
47
|
+
// Array methods that mutate the array
|
|
48
|
+
private static readonly ARRAY_MUTATORS = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
|
|
29
49
|
|
|
30
50
|
constructor(options: ReactiveEngineOptions<TContext>) {
|
|
31
|
-
|
|
32
|
-
this.
|
|
33
|
-
|
|
51
|
+
// Initialize raw state
|
|
52
|
+
this._state = {
|
|
53
|
+
context: options.initialContext,
|
|
54
|
+
facts: options.initialFacts ?? [],
|
|
55
|
+
meta: options.initialMeta ?? {},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Create reactive proxies
|
|
59
|
+
this._contextProxy = this._createReactiveProxy(this._state.context);
|
|
60
|
+
this._factsProxy = this._createReactiveProxy(this._state.facts);
|
|
61
|
+
this._metaProxy = this._createReactiveProxy(this._state.meta);
|
|
34
62
|
}
|
|
35
63
|
|
|
36
64
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
65
|
+
* Create a reactive proxy that notifies subscribers on changes.
|
|
66
|
+
* Uses a WeakMap cache to avoid creating multiple proxies for the same object.
|
|
39
67
|
*/
|
|
40
|
-
|
|
41
|
-
|
|
68
|
+
private _createReactiveProxy<T extends object>(target: T): T {
|
|
69
|
+
// Check cache first
|
|
70
|
+
const cached = this._proxyCache.get(target);
|
|
71
|
+
if (cached) {
|
|
72
|
+
return cached;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const self = this;
|
|
76
|
+
|
|
77
|
+
const handler: ProxyHandler<T> = {
|
|
78
|
+
get(obj, prop) {
|
|
79
|
+
const value = Reflect.get(obj, prop);
|
|
80
|
+
|
|
81
|
+
// If the value is an object or array, wrap it in a proxy too
|
|
82
|
+
if (value && typeof value === 'object') {
|
|
83
|
+
return self._createReactiveProxy(value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Bind array methods to notify on mutations
|
|
87
|
+
if (Array.isArray(obj) && typeof value === 'function') {
|
|
88
|
+
if (ReactiveLogicEngine.ARRAY_MUTATORS.includes(prop as string)) {
|
|
89
|
+
return function(...args: any[]) {
|
|
90
|
+
const result = (value as Function).apply(obj, args);
|
|
91
|
+
self._notify();
|
|
92
|
+
return result;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return value;
|
|
98
|
+
},
|
|
99
|
+
set(obj, prop, value) {
|
|
100
|
+
const oldValue = (obj as any)[prop];
|
|
101
|
+
const result = Reflect.set(obj, prop, value);
|
|
102
|
+
|
|
103
|
+
// Only notify if value actually changed
|
|
104
|
+
if (oldValue !== value) {
|
|
105
|
+
self._notify();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return result;
|
|
109
|
+
},
|
|
110
|
+
deleteProperty(obj, prop) {
|
|
111
|
+
const result = Reflect.deleteProperty(obj, prop);
|
|
112
|
+
self._notify();
|
|
113
|
+
return result;
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const proxy = new Proxy(target, handler);
|
|
118
|
+
|
|
119
|
+
// Cache the proxy
|
|
120
|
+
this._proxyCache.set(target, proxy);
|
|
121
|
+
|
|
122
|
+
return proxy;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Notify all subscribers of state changes
|
|
127
|
+
*/
|
|
128
|
+
private _notify() {
|
|
129
|
+
// If we're in a batch, just mark that we need to notify
|
|
130
|
+
if (this._batchDepth > 0) {
|
|
131
|
+
this._pendingNotification = true;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Pass proxy versions to subscribers so they can't bypass reactivity
|
|
136
|
+
const currentState = {
|
|
137
|
+
context: this._contextProxy,
|
|
138
|
+
facts: this._factsProxy,
|
|
139
|
+
meta: this._metaProxy,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
this._subscribers.forEach((callback) => {
|
|
143
|
+
try {
|
|
144
|
+
callback(currentState);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error('Error in reactive engine subscriber:', error);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get the full state object
|
|
153
|
+
*/
|
|
154
|
+
get state() {
|
|
155
|
+
return {
|
|
156
|
+
context: this._contextProxy,
|
|
157
|
+
facts: this._factsProxy,
|
|
158
|
+
meta: this._metaProxy,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Access the reactive context.
|
|
164
|
+
* Changes to this object will trigger subscriber notifications.
|
|
165
|
+
*/
|
|
166
|
+
get context(): TContext {
|
|
167
|
+
return this._contextProxy;
|
|
42
168
|
}
|
|
43
169
|
|
|
44
170
|
/**
|
|
45
171
|
* Access the reactive facts list.
|
|
172
|
+
* Changes to this array will trigger subscriber notifications.
|
|
173
|
+
*/
|
|
174
|
+
get facts(): any[] {
|
|
175
|
+
return this._factsProxy;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Access the reactive metadata.
|
|
180
|
+
* Changes to this object will trigger subscriber notifications.
|
|
46
181
|
*/
|
|
47
|
-
get
|
|
48
|
-
return this.
|
|
182
|
+
get meta(): Record<string, unknown> {
|
|
183
|
+
return this._metaProxy;
|
|
49
184
|
}
|
|
50
185
|
|
|
51
186
|
/**
|
|
52
187
|
* Apply a mutation to the state.
|
|
53
188
|
* This is the "Action" or "Rule" equivalent.
|
|
189
|
+
* Mutations are batched - notifications only happen once per apply call.
|
|
54
190
|
*
|
|
55
191
|
* @param mutator A function that receives the state and modifies it.
|
|
56
192
|
*/
|
|
57
|
-
apply(mutator: (state: { context: TContext; facts: any[]; meta: Record<string, unknown> }) => void) {
|
|
58
|
-
|
|
193
|
+
apply(mutator: (state: { context: TContext; facts: any[]; meta: Record<string, unknown> }) => void): void {
|
|
194
|
+
this._batchDepth++;
|
|
195
|
+
try {
|
|
196
|
+
mutator({
|
|
197
|
+
context: this._contextProxy,
|
|
198
|
+
facts: this._factsProxy,
|
|
199
|
+
meta: this._metaProxy,
|
|
200
|
+
});
|
|
201
|
+
} finally {
|
|
202
|
+
this._batchDepth--;
|
|
203
|
+
if (this._batchDepth === 0 && this._pendingNotification) {
|
|
204
|
+
this._pendingNotification = false;
|
|
205
|
+
this._notify();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
59
208
|
}
|
|
60
209
|
|
|
61
210
|
/**
|
|
62
|
-
*
|
|
211
|
+
* Subscribe to state changes.
|
|
212
|
+
* Returns an unsubscribe function.
|
|
213
|
+
*
|
|
214
|
+
* @param callback Function to call when state changes
|
|
215
|
+
* @returns Unsubscribe function
|
|
63
216
|
*/
|
|
64
|
-
|
|
65
|
-
|
|
217
|
+
subscribe(callback: StateChangeCallback<TContext>): UnsubscribeFn {
|
|
218
|
+
this._subscribers.add(callback);
|
|
219
|
+
|
|
220
|
+
// Immediately call with current state (using proxy versions)
|
|
221
|
+
try {
|
|
222
|
+
callback({
|
|
223
|
+
context: this._contextProxy,
|
|
224
|
+
facts: this._factsProxy,
|
|
225
|
+
meta: this._metaProxy,
|
|
226
|
+
});
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error('Error in reactive engine subscriber:', error);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Return unsubscribe function
|
|
232
|
+
return () => {
|
|
233
|
+
this._subscribers.delete(callback);
|
|
234
|
+
};
|
|
66
235
|
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Create a derived/computed value from the state.
|
|
239
|
+
* The selector function will be called whenever the state changes.
|
|
240
|
+
*
|
|
241
|
+
* @param selector Function to extract derived value from state
|
|
242
|
+
* @returns Object with subscribe method for reactive updates
|
|
243
|
+
*/
|
|
244
|
+
$derived<TDerived>(
|
|
245
|
+
selector: (state: { context: TContext; facts: any[]; meta: Record<string, unknown> }) => TDerived
|
|
246
|
+
): { subscribe: (callback: (value: TDerived) => void) => UnsubscribeFn } {
|
|
247
|
+
const subscribers = new Set<(value: TDerived) => void>();
|
|
248
|
+
let currentValue = selector({
|
|
249
|
+
context: this._contextProxy,
|
|
250
|
+
facts: this._factsProxy,
|
|
251
|
+
meta: this._metaProxy,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Subscribe to state changes and recompute derived value
|
|
255
|
+
this.subscribe(() => {
|
|
256
|
+
const newValue = selector({
|
|
257
|
+
context: this._contextProxy,
|
|
258
|
+
facts: this._factsProxy,
|
|
259
|
+
meta: this._metaProxy,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Only notify if value changed
|
|
263
|
+
if (newValue !== currentValue) {
|
|
264
|
+
currentValue = newValue;
|
|
265
|
+
subscribers.forEach((callback) => {
|
|
266
|
+
try {
|
|
267
|
+
callback(currentValue);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
console.error('Error in derived value subscriber:', error);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
subscribe: (callback: (value: TDerived) => void) => {
|
|
277
|
+
subscribers.add(callback);
|
|
278
|
+
try {
|
|
279
|
+
callback(currentValue); // Immediately call with current value
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.error('Error in derived value subscriber:', error);
|
|
282
|
+
}
|
|
283
|
+
return () => {
|
|
284
|
+
subscribers.delete(callback);
|
|
285
|
+
};
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Create a new reactive logic engine instance.
|
|
293
|
+
*
|
|
294
|
+
* @param options Configuration options for the reactive engine
|
|
295
|
+
* @returns A new ReactiveLogicEngine instance
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* ```typescript
|
|
299
|
+
* const engine = createReactiveEngine({
|
|
300
|
+
* initialContext: { count: 0 },
|
|
301
|
+
* initialFacts: [],
|
|
302
|
+
* initialMeta: {}
|
|
303
|
+
* });
|
|
304
|
+
*
|
|
305
|
+
* // Subscribe to changes
|
|
306
|
+
* engine.subscribe((state) => {
|
|
307
|
+
* console.log('State changed:', state);
|
|
308
|
+
* });
|
|
309
|
+
*
|
|
310
|
+
* // Mutate state (will trigger subscribers)
|
|
311
|
+
* engine.apply((state) => {
|
|
312
|
+
* state.context.count++;
|
|
313
|
+
* });
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
export function createReactiveEngine<TContext extends object>(
|
|
317
|
+
options: ReactiveEngineOptions<TContext>
|
|
318
|
+
): ReactiveLogicEngine<TContext> {
|
|
319
|
+
return new ReactiveLogicEngine(options);
|
|
67
320
|
}
|
package/src/index.browser.ts
CHANGED
|
@@ -33,6 +33,14 @@ export type { PraxisEngineOptions } from './core/engine.js';
|
|
|
33
33
|
export { LogicEngine, createPraxisEngine } from './core/engine.js';
|
|
34
34
|
export * from './core/reactive-engine.svelte.js';
|
|
35
35
|
|
|
36
|
+
// Framework-agnostic Reactive Engine
|
|
37
|
+
export {
|
|
38
|
+
ReactiveLogicEngine as FrameworkAgnosticReactiveEngine,
|
|
39
|
+
createReactiveEngine as createFrameworkAgnosticReactiveEngine,
|
|
40
|
+
type ReactiveEngineOptions as FrameworkAgnosticReactiveEngineOptions,
|
|
41
|
+
type StateChangeCallback,
|
|
42
|
+
} from './core/reactive-engine.js';
|
|
43
|
+
|
|
36
44
|
// Actors
|
|
37
45
|
export type { Actor } from './core/actors.js';
|
|
38
46
|
export { ActorManager, createTimerActor } from './core/actors.js';
|
|
@@ -102,6 +110,8 @@ export {
|
|
|
102
110
|
export type {
|
|
103
111
|
PraxisDB,
|
|
104
112
|
UnsubscribeFn,
|
|
113
|
+
PluresDBInstance,
|
|
114
|
+
PluresDBAdapterConfig,
|
|
105
115
|
EventStreamEntry,
|
|
106
116
|
PraxisDBStoreOptions,
|
|
107
117
|
StoredSchema,
|
|
@@ -113,6 +123,8 @@ export type {
|
|
|
113
123
|
export {
|
|
114
124
|
InMemoryPraxisDB,
|
|
115
125
|
createInMemoryDB,
|
|
126
|
+
PluresDBPraxisAdapter,
|
|
127
|
+
createPluresDB,
|
|
116
128
|
PraxisDBStore,
|
|
117
129
|
createPraxisDBStore,
|
|
118
130
|
PRAXIS_PATHS,
|
package/src/index.ts
CHANGED
|
@@ -76,6 +76,14 @@ export type { PraxisEngineOptions } from './core/engine.js';
|
|
|
76
76
|
export { LogicEngine, createPraxisEngine } from './core/engine.js';
|
|
77
77
|
export * from './core/reactive-engine.svelte.js';
|
|
78
78
|
|
|
79
|
+
// Framework-agnostic Reactive Engine
|
|
80
|
+
export {
|
|
81
|
+
ReactiveLogicEngine as FrameworkAgnosticReactiveEngine,
|
|
82
|
+
createReactiveEngine as createFrameworkAgnosticReactiveEngine,
|
|
83
|
+
type ReactiveEngineOptions as FrameworkAgnosticReactiveEngineOptions,
|
|
84
|
+
type StateChangeCallback,
|
|
85
|
+
} from './core/reactive-engine.js';
|
|
86
|
+
|
|
79
87
|
// Actors
|
|
80
88
|
export type { Actor } from './core/actors.js';
|
|
81
89
|
export { ActorManager, createTimerActor } from './core/actors.js';
|
|
@@ -156,6 +164,8 @@ export {
|
|
|
156
164
|
export type {
|
|
157
165
|
PraxisDB,
|
|
158
166
|
UnsubscribeFn,
|
|
167
|
+
PluresDBInstance,
|
|
168
|
+
PluresDBAdapterConfig,
|
|
159
169
|
EventStreamEntry,
|
|
160
170
|
PraxisDBStoreOptions,
|
|
161
171
|
StoredSchema,
|
|
@@ -167,6 +177,8 @@ export type {
|
|
|
167
177
|
export {
|
|
168
178
|
InMemoryPraxisDB,
|
|
169
179
|
createInMemoryDB,
|
|
180
|
+
PluresDBPraxisAdapter,
|
|
181
|
+
createPluresDB,
|
|
170
182
|
PraxisDBStore,
|
|
171
183
|
createPraxisDBStore,
|
|
172
184
|
PRAXIS_PATHS,
|
|
@@ -17,8 +17,8 @@ import { PraxisDBStore, createPraxisDBStore } from '../core/pluresdb/store.js';
|
|
|
17
17
|
|
|
18
18
|
// Re-export core pluresdb types and implementations
|
|
19
19
|
// Note: Using explicit exports to avoid circular dependency issues
|
|
20
|
-
export { InMemoryPraxisDB, createInMemoryDB } from '../core/pluresdb/adapter.js';
|
|
21
|
-
export type { PraxisDB, UnsubscribeFn } from '../core/pluresdb/adapter.js';
|
|
20
|
+
export { InMemoryPraxisDB, createInMemoryDB, PluresDBPraxisAdapter, createPluresDB } from '../core/pluresdb/adapter.js';
|
|
21
|
+
export type { PraxisDB, UnsubscribeFn, PluresDBInstance, PluresDBAdapterConfig } from '../core/pluresdb/adapter.js';
|
|
22
22
|
export {
|
|
23
23
|
PraxisDBStore,
|
|
24
24
|
createPraxisDBStore,
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Features:
|
|
8
8
|
* - Store-based API for backward compatibility
|
|
9
9
|
* - Runes-based composables for Svelte 5
|
|
10
|
+
* - createReactiveEngine for easy reactive engine setup
|
|
10
11
|
* - Snapshot support for time-travel debugging
|
|
11
12
|
* - History state pattern implementation
|
|
12
13
|
* - Automatic cleanup and subscription management
|
|
@@ -15,6 +16,13 @@
|
|
|
15
16
|
import type { LogicEngine } from '../core/engine.js';
|
|
16
17
|
import type { PraxisEvent, PraxisState } from '../core/protocol.js';
|
|
17
18
|
|
|
19
|
+
// Re-export the Svelte 5 reactive engine
|
|
20
|
+
export {
|
|
21
|
+
ReactiveLogicEngine,
|
|
22
|
+
createReactiveEngine,
|
|
23
|
+
type ReactiveEngineOptions,
|
|
24
|
+
} from '../core/reactive-engine.svelte.js';
|
|
25
|
+
|
|
18
26
|
/**
|
|
19
27
|
* Writable store interface (Svelte-compatible)
|
|
20
28
|
*/
|