@plures/praxis 2.0.0 → 2.0.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 +164 -1067
- package/dist/browser/{chunk-6MVRT7CK.js → chunk-IUEKGHQN.js} +13 -3
- package/dist/browser/index.js +1 -1
- package/dist/browser/unified/index.js +1 -1
- package/dist/node/{chunk-6MVRT7CK.js → chunk-IUEKGHQN.js} +13 -3
- package/dist/node/index.cjs +13 -3
- package/dist/node/index.js +1 -1
- package/dist/node/unified/index.cjs +13 -3
- package/dist/node/unified/index.js +1 -1
- package/docs/README.md +58 -102
- package/docs/archive/1.x/CONVERSATIONS_IMPLEMENTATION.md +207 -0
- package/docs/archive/1.x/DECISION_LEDGER_IMPLEMENTATION.md +109 -0
- package/docs/archive/1.x/DECISION_LEDGER_SUMMARY.md +424 -0
- package/docs/archive/1.x/ELEVATION_SUMMARY.md +249 -0
- package/docs/archive/1.x/FEATURE_SUMMARY.md +238 -0
- package/docs/archive/1.x/GOLDEN_PATH_IMPLEMENTATION.md +280 -0
- package/docs/archive/1.x/IMPLEMENTATION.md +166 -0
- package/docs/archive/1.x/IMPLEMENTATION_COMPLETE.md +389 -0
- package/docs/archive/1.x/IMPLEMENTATION_SUMMARY.md +59 -0
- package/docs/archive/1.x/INTEGRATION_ENHANCEMENT_SUMMARY.md +238 -0
- package/docs/archive/1.x/KNO_ENG_REFACTORING_SUMMARY.md +198 -0
- package/docs/archive/1.x/MONOREPO_SUMMARY.md +158 -0
- package/docs/archive/1.x/README.md +28 -0
- package/docs/archive/1.x/SVELTE_INTEGRATION_SUMMARY.md +415 -0
- package/docs/archive/1.x/TASK_1_COMPLETE.md +235 -0
- package/docs/archive/1.x/TASK_1_SUMMARY.md +281 -0
- package/docs/archive/1.x/VERSION_0.2.0_RELEASE_NOTES.md +288 -0
- package/docs/archive/1.x/ValidationChecklist.md +7 -0
- package/package.json +11 -5
- package/src/unified/__tests__/unified-qa.test.ts +761 -0
- package/src/unified/core.ts +19 -2
package/README.md
CHANGED
|
@@ -1,45 +1,83 @@
|
|
|
1
1
|
# Praxis
|
|
2
2
|
|
|
3
|
-
<!-- plures-readme-banner -->
|
|
4
3
|
[](https://github.com/plures/praxis/actions/workflows/ci.yml)
|
|
5
4
|
[](https://www.npmjs.com/package/@plures/praxis)
|
|
5
|
+
[](https://jsr.io/@plures/praxis)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
+
**The full-stack declarative application framework — typed logic, reactive state, local-first data, and visual tooling for Svelte, Node, and the browser.**
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
Praxis 2.0 gives you a single `createApp()` call that wires reactive state, constraint validation, rule evaluation, and an immutable timeline — zero boilerplate. When you need deeper control, drop down to the classic engine with registries, typed events/facts, and undo/redo history.
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
[](https://jsr.io/@plures/praxis)
|
|
13
|
-
[](LICENSE)
|
|
14
|
-
[](https://nodejs.org/)
|
|
15
|
-
[](https://deno.land/)
|
|
12
|
+
## Install
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
Node 18+ recommended.
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
```bash
|
|
17
|
+
npm install @plures/praxis # or: pnpm add @plures/praxis
|
|
18
|
+
```
|
|
20
19
|
|
|
20
|
+
## Quick Start — Unified App (v2.0)
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
Node 18+ recommended.
|
|
22
|
+
The fastest way to build with Praxis. Define paths (state), rules, and constraints — Praxis handles the rest.
|
|
24
23
|
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
```ts
|
|
25
|
+
import {
|
|
26
|
+
createApp,
|
|
27
|
+
definePath,
|
|
28
|
+
defineRule,
|
|
29
|
+
defineConstraint,
|
|
30
|
+
RuleResult,
|
|
31
|
+
fact,
|
|
32
|
+
} from '@plures/praxis/unified';
|
|
33
|
+
|
|
34
|
+
// 1. Declare your state shape
|
|
35
|
+
const Count = definePath<number>('count', 0);
|
|
36
|
+
const Max = definePath<number>('max', 10);
|
|
37
|
+
|
|
38
|
+
// 2. Add constraints
|
|
39
|
+
const notNegative = defineConstraint({
|
|
40
|
+
id: 'count.not-negative',
|
|
41
|
+
description: 'Count must never go below zero',
|
|
42
|
+
watch: ['count'],
|
|
43
|
+
validate: (values) => values['count'] >= 0 || 'Count cannot be negative',
|
|
44
|
+
});
|
|
28
45
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
// 3. Add rules
|
|
47
|
+
const capAtMax = defineRule({
|
|
48
|
+
id: 'count.cap',
|
|
49
|
+
watch: ['count', 'max'],
|
|
50
|
+
evaluate: (values) => {
|
|
51
|
+
if (values['count'] > values['max']) {
|
|
52
|
+
return RuleResult.emit([fact('count.capped', { at: values['max'] })]);
|
|
53
|
+
}
|
|
54
|
+
return RuleResult.noop();
|
|
55
|
+
},
|
|
56
|
+
});
|
|
32
57
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
58
|
+
// 4. Create the app
|
|
59
|
+
const app = createApp({
|
|
60
|
+
name: 'counter',
|
|
61
|
+
schema: [Count, Max],
|
|
62
|
+
rules: [capAtMax],
|
|
63
|
+
constraints: [notNegative],
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// 5. Query & mutate
|
|
67
|
+
const count = app.query<number>('count');
|
|
68
|
+
console.log(count.current); // 0
|
|
69
|
+
|
|
70
|
+
app.mutate('count', 5);
|
|
71
|
+
console.log(count.current); // 5
|
|
72
|
+
|
|
73
|
+
const result = app.mutate('count', -1);
|
|
74
|
+
console.log(result.accepted); // false — constraint rejected
|
|
40
75
|
```
|
|
41
76
|
|
|
42
|
-
##
|
|
77
|
+
## Classic Engine (full control)
|
|
78
|
+
|
|
79
|
+
For complex scenarios that need typed events, facts, actors, and undo/redo:
|
|
80
|
+
|
|
43
81
|
```ts
|
|
44
82
|
import {
|
|
45
83
|
createPraxisEngine,
|
|
@@ -68,1129 +106,188 @@ registry.registerRule(loginRule);
|
|
|
68
106
|
|
|
69
107
|
const engine = createPraxisEngine({ initialContext: { currentUser: null }, registry });
|
|
70
108
|
engine.step([Login.create({ username: 'alex' })]);
|
|
109
|
+
console.log(engine.getContext()); // { currentUser: 'alex' }
|
|
71
110
|
```
|
|
72
111
|
|
|
73
|
-
##
|
|
74
|
-
|
|
75
|
-
See all Praxis integrations working together - from schema definition to persistence, documentation, and distributed communication:
|
|
76
|
-
|
|
77
|
-
```ts
|
|
78
|
-
import {
|
|
79
|
-
createPraxisEngine,
|
|
80
|
-
PraxisRegistry,
|
|
81
|
-
defineRule,
|
|
82
|
-
createInMemoryDB,
|
|
83
|
-
createPluresDBAdapter,
|
|
84
|
-
createUnumAdapter,
|
|
85
|
-
createStateDocsGenerator,
|
|
86
|
-
schemaToCanvas,
|
|
87
|
-
} from '@plures/praxis';
|
|
88
|
-
|
|
89
|
-
// 1. Define logic with Praxis engine
|
|
90
|
-
const registry = new PraxisRegistry();
|
|
91
|
-
registry.registerRule(/* your rules */);
|
|
92
|
-
const engine = createPraxisEngine({ initialContext: {}, registry });
|
|
93
|
-
|
|
94
|
-
// 2. Add PluresDB for local-first persistence
|
|
95
|
-
const db = createInMemoryDB();
|
|
96
|
-
const pluresAdapter = createPluresDBAdapter({ db, registry });
|
|
97
|
-
pluresAdapter.attachEngine(engine);
|
|
98
|
-
|
|
99
|
-
// 3. Add Unum for distributed communication
|
|
100
|
-
const unum = await createUnumAdapter({
|
|
101
|
-
db,
|
|
102
|
-
identity: { name: 'node-1' },
|
|
103
|
-
realtime: true,
|
|
104
|
-
});
|
|
105
|
-
const channel = await unum.createChannel('app-sync');
|
|
112
|
+
## Svelte 5 Integration
|
|
106
113
|
|
|
107
|
-
|
|
108
|
-
unum.subscribeToEvents(channel.id, (event) => {
|
|
109
|
-
engine.step([event]);
|
|
110
|
-
});
|
|
114
|
+
### Runes API
|
|
111
115
|
|
|
112
|
-
// 4. Generate documentation with State-Docs
|
|
113
|
-
const docsGenerator = createStateDocsGenerator({
|
|
114
|
-
projectTitle: 'My App',
|
|
115
|
-
target: './docs',
|
|
116
|
-
});
|
|
117
|
-
const docs = docsGenerator.generateFromModule(registry.module);
|
|
118
|
-
|
|
119
|
-
// 5. Export schema to CodeCanvas for visual editing
|
|
120
|
-
const canvas = schemaToCanvas(mySchema);
|
|
121
|
-
// Canvas can be edited visually and converted back to schema
|
|
122
|
-
|
|
123
|
-
// Now you have:
|
|
124
|
-
// ✅ Logic engine running
|
|
125
|
-
// ✅ Auto-persisting to PluresDB
|
|
126
|
-
// ✅ Distributing events across nodes via Unum
|
|
127
|
-
// ✅ Auto-generated documentation
|
|
128
|
-
// ✅ Visual schema representation
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## Svelte integration (runes-ready)
|
|
132
116
|
```svelte
|
|
133
117
|
<script lang="ts">
|
|
134
|
-
import {
|
|
135
|
-
|
|
136
|
-
const Increment = defineEvent<'INCREMENT', { amount: number }>('INCREMENT');
|
|
137
|
-
const counterRule = defineRule<{ count: number }>({
|
|
138
|
-
id: 'counter.increment',
|
|
139
|
-
description: 'Add to count',
|
|
140
|
-
impl: (state, events) => {
|
|
141
|
-
const evt = events.find(Increment.is);
|
|
142
|
-
if (evt) state.context.count += evt.payload.amount;
|
|
143
|
-
return [];
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const registry = new PraxisRegistry();
|
|
148
|
-
registry.registerRule(counterRule);
|
|
149
|
-
|
|
150
|
-
const engine = createReactiveEngine({ initialContext: { count: 0 }, registry });
|
|
151
|
-
|
|
152
|
-
// Use Svelte's $derived with the reactive engine state
|
|
153
|
-
const count = $derived(engine.context.count);
|
|
118
|
+
import { usePraxisEngine } from '@plures/praxis/svelte';
|
|
119
|
+
import { createMyEngine, Login } from './my-engine';
|
|
154
120
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
121
|
+
const engine = createMyEngine();
|
|
122
|
+
const { context, dispatch, undo, redo, canUndo, canRedo } = usePraxisEngine(engine, {
|
|
123
|
+
enableHistory: true,
|
|
124
|
+
maxHistorySize: 50,
|
|
125
|
+
});
|
|
158
126
|
</script>
|
|
159
127
|
|
|
160
|
-
<
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
For non-Svelte environments, use the framework-agnostic reactive engine with Proxy-based reactivity:
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
import { createFrameworkAgnosticReactiveEngine } from '@plures/praxis';
|
|
168
|
-
|
|
169
|
-
const engine = createFrameworkAgnosticReactiveEngine({
|
|
170
|
-
initialContext: { count: 0 },
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// Subscribe to state changes
|
|
174
|
-
engine.subscribe((state) => {
|
|
175
|
-
console.log('Count:', state.context.count);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// Create derived/computed values
|
|
179
|
-
const doubled = engine.$derived((state) => state.context.count * 2);
|
|
180
|
-
doubled.subscribe((value) => {
|
|
181
|
-
console.log('Doubled:', value);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// Apply mutations (batched for performance)
|
|
185
|
-
engine.apply((state) => {
|
|
186
|
-
state.context.count += 1;
|
|
187
|
-
});
|
|
128
|
+
<p>User: {context.currentUser || 'Guest'}</p>
|
|
129
|
+
<button onclick={() => dispatch([Login.create({ username: 'alice' })])}>Login</button>
|
|
130
|
+
<button onclick={undo} disabled={!canUndo}>⟲ Undo</button>
|
|
131
|
+
<button onclick={redo} disabled={!canRedo}>⟳ Redo</button>
|
|
188
132
|
```
|
|
189
133
|
|
|
190
|
-
|
|
134
|
+
### Store API
|
|
191
135
|
|
|
192
|
-
## Cloud relay (optional)
|
|
193
136
|
```ts
|
|
194
|
-
import {
|
|
195
|
-
|
|
196
|
-
const relay = await connectRelay('https://my-relay.example.com', {
|
|
197
|
-
appId: 'my-app',
|
|
198
|
-
authToken: process.env.GITHUB_TOKEN,
|
|
199
|
-
autoSync: true,
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
await relay.sync({
|
|
203
|
-
type: 'delta',
|
|
204
|
-
appId: 'my-app',
|
|
205
|
-
clock: {},
|
|
206
|
-
facts: [],
|
|
207
|
-
timestamp: Date.now(),
|
|
208
|
-
});
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## PluresDB integration
|
|
212
|
-
```ts
|
|
213
|
-
import { PluresNode } from 'pluresdb';
|
|
214
|
-
import { createPluresDB, createPraxisDBStore } from '@plures/praxis';
|
|
215
|
-
import { PraxisRegistry } from '@plures/praxis';
|
|
216
|
-
|
|
217
|
-
// Initialize the official PluresDB from npm
|
|
218
|
-
const pluresdb = new PluresNode({
|
|
219
|
-
config: {
|
|
220
|
-
port: 34567,
|
|
221
|
-
dataDir: './data',
|
|
222
|
-
},
|
|
223
|
-
autoStart: true,
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// Wrap it with the Praxis adapter
|
|
227
|
-
const db = createPluresDB(pluresdb);
|
|
228
|
-
|
|
229
|
-
// Use with Praxis store for local-first reactive data
|
|
230
|
-
const registry = new PraxisRegistry();
|
|
231
|
-
const store = createPraxisDBStore(db, registry);
|
|
137
|
+
import { createPraxisStore, createDerivedStore } from '@plures/praxis/svelte';
|
|
232
138
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const devDb = createInMemoryDB();
|
|
139
|
+
const stateStore = createPraxisStore(engine);
|
|
140
|
+
const userStore = createDerivedStore(engine, (ctx) => ctx.currentUser);
|
|
236
141
|
```
|
|
237
142
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
## CLI (npx-friendly)
|
|
241
|
-
```bash
|
|
242
|
-
npx praxis --help
|
|
243
|
-
npx praxis create app my-app
|
|
244
|
-
npx praxis generate --schema src/schemas/app.schema.ts
|
|
245
|
-
npx praxis canvas src/schemas/app.schema.ts
|
|
246
|
-
```
|
|
143
|
+
See [Svelte Integration Guide](./docs/guides/svelte-integration.md) for the full API including reactive engine, history patterns, and keyboard shortcuts.
|
|
247
144
|
|
|
248
145
|
## Decision Ledger (Behavior Contracts)
|
|
249
146
|
|
|
250
|
-
|
|
147
|
+
Attach explicit contracts to every rule and constraint — with Given/When/Then examples, invariants, and assumption tracking.
|
|
251
148
|
|
|
252
|
-
```
|
|
149
|
+
```ts
|
|
253
150
|
import { defineContract, defineRule } from '@plures/praxis';
|
|
254
151
|
|
|
255
|
-
// Define a contract with explicit behavior, examples, and invariants
|
|
256
152
|
const loginContract = defineContract({
|
|
257
153
|
ruleId: 'auth.login',
|
|
258
154
|
behavior: 'Process login events and create user session facts',
|
|
259
155
|
examples: [
|
|
260
|
-
{
|
|
261
|
-
given: 'User provides valid credentials',
|
|
262
|
-
when: 'LOGIN event is received',
|
|
263
|
-
then: 'UserSessionCreated fact is emitted'
|
|
264
|
-
}
|
|
156
|
+
{ given: 'Valid credentials', when: 'LOGIN event', then: 'UserSessionCreated fact emitted' }
|
|
265
157
|
],
|
|
266
158
|
invariants: ['Session must have unique ID'],
|
|
267
|
-
assumptions: [
|
|
268
|
-
{
|
|
269
|
-
id: 'assume-unique-username',
|
|
270
|
-
statement: 'Usernames are unique across the system',
|
|
271
|
-
confidence: 0.9,
|
|
272
|
-
justification: 'Standard authentication practice',
|
|
273
|
-
impacts: ['spec', 'tests', 'code'],
|
|
274
|
-
status: 'active'
|
|
275
|
-
}
|
|
276
|
-
]
|
|
277
159
|
});
|
|
278
160
|
|
|
279
|
-
// Attach contract to rule
|
|
280
161
|
const loginRule = defineRule({
|
|
281
162
|
id: 'auth.login',
|
|
282
163
|
description: 'Process login events',
|
|
283
164
|
impl: (state, events) => { /* ... */ },
|
|
284
|
-
contract: loginContract
|
|
165
|
+
contract: loginContract,
|
|
285
166
|
});
|
|
286
167
|
```
|
|
287
168
|
|
|
288
|
-
**Validate contracts in CI/CD:**
|
|
289
169
|
```bash
|
|
290
|
-
#
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
# Generate SARIF for GitHub Actions
|
|
294
|
-
npx praxis validate --output sarif > results.sarif
|
|
295
|
-
|
|
296
|
-
# Reverse engineer contracts from existing code
|
|
297
|
-
npx praxis reverse --interactive
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
**Key features:**
|
|
301
|
-
- ✅ Explicit behavior documentation with Given/When/Then examples
|
|
302
|
-
- ✅ Assumption tracking with confidence levels
|
|
303
|
-
- ✅ Immutable ledger for change history
|
|
304
|
-
- ✅ Build-time validation and CI/CD integration
|
|
305
|
-
- ✅ Auto-generation from existing code
|
|
306
|
-
|
|
307
|
-
See [src/decision-ledger/README.md](./src/decision-ledger/README.md) for complete documentation.
|
|
308
|
-
|
|
309
|
-
## Exports map
|
|
310
|
-
- `@plures/praxis` → main engine (ESM/CJS/types)
|
|
311
|
-
- `@plures/praxis/svelte` → Svelte 5 integrations
|
|
312
|
-
- `@plures/praxis/schema` → Schema types
|
|
313
|
-
- `@plures/praxis/component` → Component generator
|
|
314
|
-
- `@plures/praxis/cloud` → Cloud relay APIs
|
|
315
|
-
- `@plures/praxis/components` → TS props for Svelte components (e.g., TerminalNode)
|
|
316
|
-
- `praxis` (bin) → CLI entrypoint
|
|
317
|
-
|
|
318
|
-
## Documentation
|
|
319
|
-
- [Getting Started](./GETTING_STARTED.md)
|
|
320
|
-
- [Framework Guide](./FRAMEWORK.md)
|
|
321
|
-
- [Praxis-Core API](./docs/core/praxis-core-api.md) - Stable API surface & guarantees
|
|
322
|
-
- [Extending Praxis-Core](./docs/core/extending-praxis-core.md) - Extension guidelines
|
|
323
|
-
- [Decision Ledger Guide](./src/decision-ledger/README.md)
|
|
324
|
-
- [Examples](./examples/)
|
|
325
|
-
|
|
326
|
-
## Decision Ledger
|
|
327
|
-
|
|
328
|
-
Praxis dogfoods its Decision Ledger to keep rule/constraint behavior explicit and enforceable.
|
|
329
|
-
|
|
330
|
-
- [Behavior Ledger](./docs/decision-ledger/BEHAVIOR_LEDGER.md)
|
|
331
|
-
- [Dogfooding Guide](./docs/decision-ledger/DOGFOODING.md)
|
|
332
|
-
|
|
333
|
-
## Contributing
|
|
334
|
-
PRs and discussions welcome. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) and [SECURITY.md](./SECURITY.md).
|
|
335
|
-
console.log(result.state.facts); // [{ tag: "UserLoggedIn", payload: { userId: "alice" } }]
|
|
336
|
-
console.log(engine.getContext()); // { currentUser: "alice" }
|
|
170
|
+
npm run scan:rules # Index all rules/constraints
|
|
171
|
+
npm run validate:contracts # Validate all contracts
|
|
172
|
+
npx praxis validate --strict # CI/CD enforcement
|
|
337
173
|
```
|
|
338
174
|
|
|
339
|
-
|
|
175
|
+
See [Decision Ledger Guide](./docs/decision-ledger/DOGFOODING.md) for the full dogfooding workflow.
|
|
340
176
|
|
|
341
|
-
|
|
342
|
-
import { defineConstraint } from '@plures/praxis';
|
|
177
|
+
## PluresDB — Local-First Persistence
|
|
343
178
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
description: 'Only one user can be logged in at a time',
|
|
347
|
-
impl: (state) => {
|
|
348
|
-
return state.context.currentUser === null || 'User already logged in';
|
|
349
|
-
},
|
|
350
|
-
});
|
|
179
|
+
```ts
|
|
180
|
+
import { createInMemoryDB, createPluresDBAdapter } from '@plures/praxis';
|
|
351
181
|
|
|
352
|
-
|
|
182
|
+
const db = createInMemoryDB();
|
|
183
|
+
const adapter = createPluresDBAdapter({ db, registry });
|
|
184
|
+
adapter.attachEngine(engine); // auto-persist facts & events
|
|
353
185
|
```
|
|
354
186
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
#### Store API (Svelte 4/5 Compatible)
|
|
187
|
+
For production, use the official [@plures/pluresdb](https://www.npmjs.com/package/@plures/pluresdb) package with P2P sync and CRDT conflict resolution:
|
|
358
188
|
|
|
359
|
-
```
|
|
360
|
-
import {
|
|
361
|
-
|
|
362
|
-
const stateStore = createPraxisStore(engine);
|
|
363
|
-
const userStore = createDerivedStore(engine, (ctx) => ctx.currentUser);
|
|
189
|
+
```ts
|
|
190
|
+
import { PluresNode } from '@plures/pluresdb';
|
|
191
|
+
import { createPluresDB } from '@plures/praxis';
|
|
364
192
|
|
|
365
|
-
|
|
366
|
-
// $: currentUser = $userStore;
|
|
367
|
-
// <button on:click={() => stateStore.dispatch([Login.create({ username: "alice" })])}>
|
|
368
|
-
// Login
|
|
369
|
-
// </button>
|
|
193
|
+
const db = createPluresDB(new PluresNode({ config: { port: 34567, dataDir: './data' }, autoStart: true }));
|
|
370
194
|
```
|
|
371
195
|
|
|
372
|
-
|
|
196
|
+
## CLI
|
|
373
197
|
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const engine = createMyEngine();
|
|
380
|
-
const {
|
|
381
|
-
context, // Reactive context
|
|
382
|
-
dispatch, // Dispatch events
|
|
383
|
-
undo, // Undo last action
|
|
384
|
-
redo, // Redo action
|
|
385
|
-
canUndo, // Boolean: can undo?
|
|
386
|
-
canRedo, // Boolean: can redo?
|
|
387
|
-
} = usePraxisEngine(engine, {
|
|
388
|
-
enableHistory: true, // Enable undo/redo
|
|
389
|
-
maxHistorySize: 50, // Keep last 50 snapshots
|
|
390
|
-
});
|
|
391
|
-
</script>
|
|
392
|
-
|
|
393
|
-
<div>
|
|
394
|
-
<p>User: {context.currentUser || 'Guest'}</p>
|
|
395
|
-
|
|
396
|
-
<button onclick={() => dispatch([Login.create({ username: 'alice' })])}>
|
|
397
|
-
Login
|
|
398
|
-
</button>
|
|
399
|
-
|
|
400
|
-
<button onclick={undo} disabled={!canUndo}>
|
|
401
|
-
⟲ Undo
|
|
402
|
-
</button>
|
|
403
|
-
|
|
404
|
-
<button onclick={redo} disabled={!canRedo}>
|
|
405
|
-
⟳ Redo
|
|
406
|
-
</button>
|
|
407
|
-
</div>
|
|
198
|
+
```bash
|
|
199
|
+
npx praxis --help
|
|
200
|
+
npx praxis create app my-app
|
|
201
|
+
npx praxis generate --schema src/schemas/app.schema.ts
|
|
202
|
+
npx praxis canvas src/schemas/app.schema.ts
|
|
408
203
|
```
|
|
409
204
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
- Undo/redo functionality
|
|
413
|
-
- Time-travel debugging
|
|
414
|
-
- Keyboard shortcuts
|
|
415
|
-
- Beautiful UI
|
|
205
|
+
## Exports
|
|
416
206
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
207
|
+
| Import path | Description |
|
|
208
|
+
|---|---|
|
|
209
|
+
| `@plures/praxis` | Core engine, DSL, protocol types, integrations |
|
|
210
|
+
| `@plures/praxis/unified` | `createApp()` unified reactive layer |
|
|
211
|
+
| `@plures/praxis/svelte` | Svelte 5 runes + store APIs |
|
|
212
|
+
| `@plures/praxis/schema` | Schema types and loaders |
|
|
213
|
+
| `@plures/praxis/component` | Svelte component generator |
|
|
214
|
+
| `@plures/praxis/cloud` | Cloud relay sync |
|
|
215
|
+
| `@plures/praxis/components` | Built-in Svelte components |
|
|
216
|
+
| `@plures/praxis/mcp` | Model Context Protocol tools |
|
|
217
|
+
| `@plures/praxis/expectations` | Behavior expectation DSL |
|
|
218
|
+
| `@plures/praxis/factory` | Pre-built UI rule modules |
|
|
219
|
+
| `@plures/praxis/project` | Project gates and lifecycle |
|
|
220
|
+
| `praxis` (bin) | CLI entrypoint |
|
|
422
221
|
|
|
423
222
|
## Core Protocol
|
|
424
223
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
```typescript
|
|
428
|
-
// Facts and Events
|
|
429
|
-
interface PraxisFact {
|
|
430
|
-
tag: string;
|
|
431
|
-
payload: unknown;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
interface PraxisEvent {
|
|
435
|
-
tag: string;
|
|
436
|
-
payload: unknown;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// State
|
|
440
|
-
interface PraxisState {
|
|
441
|
-
context: unknown;
|
|
442
|
-
facts: PraxisFact[];
|
|
443
|
-
meta?: Record<string, unknown>;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// Step Function (the conceptual core)
|
|
447
|
-
type PraxisStepFn = (
|
|
448
|
-
state: PraxisState,
|
|
449
|
-
events: PraxisEvent[],
|
|
450
|
-
config: PraxisStepConfig
|
|
451
|
-
) => PraxisStepResult;
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
This protocol is:
|
|
455
|
-
|
|
456
|
-
- Pure and deterministic (data in → data out)
|
|
457
|
-
- No side effects, no global state
|
|
458
|
-
- JSON-friendly for cross-language compatibility
|
|
459
|
-
- The foundation for all higher-level TypeScript APIs
|
|
460
|
-
|
|
461
|
-
## Framework Architecture
|
|
462
|
-
|
|
463
|
-
Praxis is organized as a **monorepo** with clearly separated packages. See [MONOREPO.md](./MONOREPO.md) for the complete organization plan.
|
|
464
|
-
|
|
465
|
-
### Target Monorepo Structure
|
|
466
|
-
|
|
467
|
-
```
|
|
468
|
-
praxis/
|
|
469
|
-
├── packages/ # Published npm packages
|
|
470
|
-
│ ├── praxis-core/ # Core logic library (zero dependencies)
|
|
471
|
-
│ │ └── src/
|
|
472
|
-
│ │ ├── logic/ # Facts, events, rules, constraints, engine
|
|
473
|
-
│ │ ├── schema/ # Schema definitions and validation
|
|
474
|
-
│ │ ├── decision-ledger/ # Contracts and behavior specifications
|
|
475
|
-
│ │ └── protocol/ # Core protocol types
|
|
476
|
-
│ ├── praxis-cli/ # Command-line interface
|
|
477
|
-
│ │ └── src/
|
|
478
|
-
│ │ ├── commands/ # CLI commands
|
|
479
|
-
│ │ └── generators/ # Code generators
|
|
480
|
-
│ ├── praxis-svelte/ # Svelte 5 integration
|
|
481
|
-
│ │ └── src/
|
|
482
|
-
│ │ ├── components/ # Reactive Svelte components
|
|
483
|
-
│ │ ├── generators/ # Component generators
|
|
484
|
-
│ │ └── runtime/ # Svelte runtime integration
|
|
485
|
-
│ ├── praxis-cloud/ # Cloud sync and relay
|
|
486
|
-
│ │ └── src/
|
|
487
|
-
│ │ ├── relay/ # Cloud relay server
|
|
488
|
-
│ │ └── sync/ # Sync protocol
|
|
489
|
-
│ └── praxis/ # Main package (re-exports all)
|
|
490
|
-
├── apps/ # Example applications
|
|
491
|
-
├── tools/ # Development tools
|
|
492
|
-
├── ui/ # UI components and tools
|
|
493
|
-
├── docs/ # Documentation
|
|
494
|
-
└── examples/ # Simple examples and demos
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
### Current Structure (In Transition)
|
|
224
|
+
All Praxis APIs build on a language-neutral, pure-function protocol:
|
|
498
225
|
|
|
499
|
-
|
|
226
|
+
```ts
|
|
227
|
+
interface PraxisFact { tag: string; payload: unknown }
|
|
228
|
+
interface PraxisEvent { tag: string; payload: unknown }
|
|
229
|
+
interface PraxisState { context: unknown; facts: PraxisFact[]; meta?: Record<string, unknown> }
|
|
500
230
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
├── core/ # Core framework
|
|
504
|
-
│ ├── schema/ # Schema system
|
|
505
|
-
│ │ └── types.ts # Schema type definitions
|
|
506
|
-
│ ├── logic/ # Logic engine (existing src/core/)
|
|
507
|
-
│ │ ├── protocol.ts # Language-neutral protocol
|
|
508
|
-
│ │ ├── rules.ts # Rules, constraints, and registry
|
|
509
|
-
│ │ ├── engine.ts # LogicEngine implementation
|
|
510
|
-
│ │ ├── actors.ts # Actor system
|
|
511
|
-
│ │ └── introspection.ts # Introspection and visualization
|
|
512
|
-
│ ├── component/ # Component generation
|
|
513
|
-
│ │ └── generator.ts # Svelte component generator
|
|
514
|
-
│ ├── pluresdb/ # PluresDB integration core
|
|
515
|
-
│ │ ├── adapter.ts # Database adapter interface
|
|
516
|
-
│ │ ├── store.ts # Reactive store implementation
|
|
517
|
-
│ │ ├── schema-registry.ts # Schema registry for PluresDB
|
|
518
|
-
│ │ └── generator.ts # PluresDB config generator
|
|
519
|
-
│ └── runtime/ # Runtime abstractions
|
|
520
|
-
├── cloud/ # Praxis Cloud integration
|
|
521
|
-
│ ├── auth.ts # GitHub OAuth authentication
|
|
522
|
-
│ ├── billing.ts # Tier-based billing
|
|
523
|
-
│ ├── provisioning.ts # Tenant provisioning
|
|
524
|
-
│ └── relay/ # Azure relay service
|
|
525
|
-
├── integrations/ # Ecosystem integrations
|
|
526
|
-
│ ├── pluresdb.ts # PluresDB integration exports
|
|
527
|
-
│ ├── svelte.ts # Svelte 5 integration
|
|
528
|
-
│ ├── unum/ # Unum identity and channels
|
|
529
|
-
│ ├── adp/ # Architectural Decision Protocol
|
|
530
|
-
│ ├── state-docs/ # State-Docs documentation
|
|
531
|
-
│ └── canvas/ # CodeCanvas visual editor
|
|
532
|
-
├── components/ # Svelte components
|
|
533
|
-
│ └── TerminalNode.svelte # Terminal node component
|
|
534
|
-
├── cli/ # Command-line interface
|
|
535
|
-
│ ├── index.ts # CLI entry point
|
|
536
|
-
│ └── commands/ # Command implementations
|
|
537
|
-
├── templates/ # Project templates
|
|
538
|
-
│ ├── basic-app/ # Basic application template
|
|
539
|
-
│ └── fullstack-app/ # Full-stack template
|
|
540
|
-
├── examples/ # Example applications
|
|
541
|
-
│ ├── offline-chat/ # Offline-first chat demo
|
|
542
|
-
│ ├── knowledge-canvas/ # Knowledge management with Canvas
|
|
543
|
-
│ ├── distributed-node/ # Self-orchestrating node demo
|
|
544
|
-
│ ├── terminal-node/ # Terminal node demo
|
|
545
|
-
│ ├── terminal-canvas/ # Terminal + canvas demo
|
|
546
|
-
│ ├── cloud-sync/ # Cloud sync demo
|
|
547
|
-
│ ├── github-monetization/ # GitHub monetization demo
|
|
548
|
-
│ ├── simple-app/ # Simple app demo
|
|
549
|
-
│ ├── auth-basic/ # Login/logout example
|
|
550
|
-
│ ├── cart/ # Shopping cart example
|
|
551
|
-
│ ├── svelte-counter/ # Svelte integration example
|
|
552
|
-
│ └── hero-ecommerce/ # Comprehensive e-commerce demo
|
|
553
|
-
└── docs/ # Framework documentation
|
|
554
|
-
├── guides/ # User guides
|
|
555
|
-
│ ├── getting-started.md # Getting started guide
|
|
556
|
-
│ ├── canvas.md # CodeCanvas guide
|
|
557
|
-
│ └── orchestration.md # Orchestration guide
|
|
558
|
-
├── api/ # API reference
|
|
559
|
-
└── architecture/ # Architecture documentation
|
|
231
|
+
// The conceptual core — pure, deterministic, no side effects
|
|
232
|
+
type PraxisStepFn = (state: PraxisState, events: PraxisEvent[], config: PraxisStepConfig) => PraxisStepResult;
|
|
560
233
|
```
|
|
561
234
|
|
|
562
|
-
See [
|
|
235
|
+
Implemented in TypeScript (npm), C# (.NET 8+, NuGet: `Plures.Praxis`), and PowerShell. All share the same JSON wire format. See [CROSS_LANGUAGE_SYNC.md](./CROSS_LANGUAGE_SYNC.md) and [PROTOCOL_VERSIONING.md](./PROTOCOL_VERSIONING.md).
|
|
563
236
|
|
|
564
237
|
## Examples
|
|
565
238
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
-
|
|
574
|
-
|
|
575
|
-
-
|
|
576
|
-
- Actors for logging and analytics
|
|
577
|
-
- Constraints enforcing business rules
|
|
578
|
-
|
|
579
|
-
```bash
|
|
580
|
-
npm run build
|
|
581
|
-
node dist/examples/hero-ecommerce/index.js
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
### 2. Offline-First Chat (`examples/offline-chat`)
|
|
585
|
-
|
|
586
|
-
Demonstrates local-first architecture with PluresDB:
|
|
587
|
-
|
|
588
|
-
- Offline message composition and storage
|
|
589
|
-
- Automatic sync when connected
|
|
590
|
-
- Message queue for offline messages
|
|
591
|
-
- Conflict resolution for concurrent edits
|
|
592
|
-
- Real-time features (typing indicators, read receipts)
|
|
593
|
-
|
|
594
|
-
See [examples/offline-chat/README.md](./examples/offline-chat/README.md)
|
|
595
|
-
|
|
596
|
-
### 3. Knowledge Canvas (`examples/knowledge-canvas`)
|
|
597
|
-
|
|
598
|
-
Showcases CodeCanvas integration for visual knowledge management:
|
|
599
|
-
|
|
600
|
-
- Visual knowledge graph editing
|
|
601
|
-
- Schema-driven content types
|
|
602
|
-
- Generated UI components
|
|
603
|
-
- State-Docs integration
|
|
604
|
-
- Collaborative editing
|
|
605
|
-
|
|
606
|
-
See [examples/knowledge-canvas/README.md](./examples/knowledge-canvas/README.md)
|
|
607
|
-
|
|
608
|
-
### 4. Self-Orchestrating Node (`examples/distributed-node`)
|
|
609
|
-
|
|
610
|
-
Demonstrates distributed orchestration with DSC/MCP:
|
|
611
|
-
|
|
612
|
-
- Automatic node discovery
|
|
613
|
-
- Self-healing behavior
|
|
614
|
-
- State synchronization across nodes
|
|
615
|
-
- Health monitoring and auto-scaling
|
|
616
|
-
- Failover and recovery
|
|
617
|
-
|
|
618
|
-
See [examples/distributed-node/README.md](./examples/distributed-node/README.md)
|
|
619
|
-
|
|
620
|
-
### 5. Terminal Node (`examples/terminal-node`)
|
|
621
|
-
|
|
622
|
-
Demonstrates the terminal node feature for command execution:
|
|
623
|
-
|
|
624
|
-
- Terminal adapter creation and configuration
|
|
625
|
-
- Command execution and history tracking
|
|
626
|
-
- YAML schema loading with terminal nodes
|
|
627
|
-
- PluresDB binding configuration
|
|
628
|
-
- Both text and widget input modes
|
|
629
|
-
|
|
630
|
-
```bash
|
|
631
|
-
npm run build
|
|
632
|
-
node examples/terminal-node/index.js
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
See [examples/terminal-node/README.md](./examples/terminal-node/README.md) and [docs/TERMINAL_NODE.md](./docs/TERMINAL_NODE.md)
|
|
636
|
-
|
|
637
|
-
### 6. Auth Basic (`src/examples/auth-basic`)
|
|
638
|
-
|
|
639
|
-
Login/logout with facts, rules, and constraints.
|
|
640
|
-
|
|
641
|
-
```bash
|
|
642
|
-
npm run build
|
|
643
|
-
node dist/examples/auth-basic/index.js
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
### 7. Cart (`src/examples/cart`)
|
|
647
|
-
|
|
648
|
-
Shopping cart with multiple rules, constraints, and complex state management.
|
|
649
|
-
|
|
650
|
-
```bash
|
|
651
|
-
npm run build
|
|
652
|
-
node dist/examples/cart/index.js
|
|
653
|
-
```
|
|
654
|
-
|
|
655
|
-
### 8. Svelte Counter (`src/examples/svelte-counter`)
|
|
656
|
-
|
|
657
|
-
Counter example showing Svelte v5 integration with reactive stores.
|
|
658
|
-
|
|
659
|
-
```bash
|
|
660
|
-
npm run build
|
|
661
|
-
node dist/examples/svelte-counter/index.js
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
### 9. Terminal Canvas (`examples/terminal-canvas`)
|
|
665
|
-
|
|
666
|
-
Combines terminal nodes with visual canvas features in a Svelte app.
|
|
239
|
+
| Example | Description | Location |
|
|
240
|
+
|---|---|---|
|
|
241
|
+
| Hero E-Commerce | Full-stack: auth, cart, discounts, loyalty, actors | `src/examples/hero-ecommerce/` |
|
|
242
|
+
| Decision Ledger | Contracts, validation, SARIF output | `examples/decision-ledger/` |
|
|
243
|
+
| Offline Chat | Local-first messaging with PluresDB | `examples/offline-chat/` |
|
|
244
|
+
| Terminal Node | Command execution with YAML schemas | `examples/terminal-node/` |
|
|
245
|
+
| Unified App | `createApp()` + rules + Mermaid docs | `examples/unified-app/` |
|
|
246
|
+
| Cloud Sync | Real-time relay synchronization | `examples/cloud-sync/` |
|
|
247
|
+
| Simple App | Minimal schema usage | `examples/simple-app/` |
|
|
248
|
+
| Reactive Counter | Framework-agnostic reactive engine | `examples/reactive-counter/` |
|
|
667
249
|
|
|
668
|
-
|
|
250
|
+
Browse all examples in [`examples/`](./examples/) and [`src/examples/`](./src/examples/).
|
|
669
251
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
Example of GitHub-based monetization integration with Praxis Cloud.
|
|
673
|
-
|
|
674
|
-
See [examples/github-monetization/README.md](./examples/github-monetization/README.md)
|
|
675
|
-
|
|
676
|
-
### 11. Simple App (`examples/simple-app`)
|
|
677
|
-
|
|
678
|
-
A minimal example demonstrating basic Praxis schema usage.
|
|
679
|
-
|
|
680
|
-
See [examples/simple-app/README.md](./examples/simple-app/README.md)
|
|
681
|
-
|
|
682
|
-
### 12. Cloud Sync (`examples/cloud-sync`)
|
|
683
|
-
|
|
684
|
-
Demonstrates real-time synchronization with Praxis Cloud relay service.
|
|
685
|
-
|
|
686
|
-
See [examples/cloud-sync/README.md](./examples/cloud-sync/README.md)
|
|
687
|
-
|
|
688
|
-
### 13. Decision Ledger (`examples/decision-ledger`)
|
|
689
|
-
|
|
690
|
-
Demonstrates behavior contracts for rules and constraints with validation and immutable ledger tracking.
|
|
691
|
-
|
|
692
|
-
Features:
|
|
693
|
-
- Contract definition with behavior, examples, and invariants
|
|
694
|
-
- Assumption tracking with confidence levels
|
|
695
|
-
- Validation and reporting (console, JSON, SARIF)
|
|
696
|
-
- Immutable logic ledger for change history
|
|
697
|
-
- CLI integration for CI/CD pipelines
|
|
698
|
-
|
|
699
|
-
```bash
|
|
700
|
-
npm run build
|
|
701
|
-
node examples/decision-ledger/index.js
|
|
702
|
-
|
|
703
|
-
# Validate contracts
|
|
704
|
-
npx praxis validate --registry examples/sample-registry.js
|
|
705
|
-
```
|
|
706
|
-
|
|
707
|
-
See [examples/decision-ledger/README.md](./examples/decision-ledger/README.md)
|
|
708
|
-
|
|
709
|
-
## API Reference
|
|
710
|
-
|
|
711
|
-
### Core Types
|
|
712
|
-
|
|
713
|
-
- `PraxisFact`, `PraxisEvent`, `PraxisState` - Protocol types
|
|
714
|
-
- `LogicEngine<TContext>` - Main engine class
|
|
715
|
-
- `PraxisRegistry<TContext>` - Rule and constraint registry
|
|
716
|
-
- `Actor<TContext>` - Actor interface
|
|
717
|
-
- `ActorManager<TContext>` - Actor lifecycle management
|
|
718
|
-
|
|
719
|
-
### DSL Functions
|
|
720
|
-
|
|
721
|
-
- `defineFact<TTag, TPayload>(tag)` - Define a typed fact
|
|
722
|
-
- `defineEvent<TTag, TPayload>(tag)` - Define a typed event
|
|
723
|
-
- `defineRule<TContext>(options)` - Define a rule
|
|
724
|
-
- `defineConstraint<TContext>(options)` - Define a constraint
|
|
725
|
-
- `defineModule<TContext>(options)` - Bundle rules and constraints
|
|
726
|
-
|
|
727
|
-
### Helpers
|
|
728
|
-
|
|
729
|
-
- `findEvent(events, definition)` - Find first matching event
|
|
730
|
-
- `findFact(facts, definition)` - Find first matching fact
|
|
731
|
-
- `filterEvents(events, definition)` - Filter events by type
|
|
732
|
-
- `filterFacts(facts, definition)` - Filter facts by type
|
|
733
|
-
|
|
734
|
-
### Introspection & Visualization
|
|
735
|
-
|
|
736
|
-
Tools for examining and visualizing your Praxis logic:
|
|
737
|
-
|
|
738
|
-
```typescript
|
|
739
|
-
import { createIntrospector, PRAXIS_PROTOCOL_VERSION } from '@plures/praxis';
|
|
740
|
-
|
|
741
|
-
const introspector = createIntrospector(registry);
|
|
742
|
-
|
|
743
|
-
// Get statistics
|
|
744
|
-
const stats = introspector.getStats();
|
|
745
|
-
console.log(`Rules: ${stats.ruleCount}, Constraints: ${stats.constraintCount}`);
|
|
746
|
-
|
|
747
|
-
// Generate JSON schema
|
|
748
|
-
const schema = introspector.generateSchema(PRAXIS_PROTOCOL_VERSION);
|
|
749
|
-
|
|
750
|
-
// Generate graph visualization
|
|
751
|
-
const graph = introspector.generateGraph();
|
|
752
|
-
|
|
753
|
-
// Export to Graphviz DOT format
|
|
754
|
-
const dot = introspector.exportDOT();
|
|
755
|
-
fs.writeFileSync('registry.dot', dot);
|
|
756
|
-
|
|
757
|
-
// Export to Mermaid format
|
|
758
|
-
const mermaid = introspector.exportMermaid();
|
|
759
|
-
|
|
760
|
-
// Search rules and constraints
|
|
761
|
-
const authRules = introspector.searchRules('auth');
|
|
762
|
-
const maxConstraints = introspector.searchConstraints('max');
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
**Available methods:**
|
|
766
|
-
|
|
767
|
-
- `getStats()` - Get registry statistics
|
|
768
|
-
- `generateSchema(protocolVersion)` - Generate JSON schema
|
|
769
|
-
- `generateGraph()` - Generate graph representation
|
|
770
|
-
- `exportDOT()` - Export to Graphviz DOT format
|
|
771
|
-
- `exportMermaid()` - Export to Mermaid diagram format
|
|
772
|
-
- `getRuleInfo(id)` - Get detailed rule information
|
|
773
|
-
- `getConstraintInfo(id)` - Get detailed constraint information
|
|
774
|
-
- `searchRules(query)` - Search rules by text
|
|
775
|
-
- `searchConstraints(query)` - Search constraints by text
|
|
776
|
-
|
|
777
|
-
## Ecosystem Integration
|
|
778
|
-
|
|
779
|
-
Praxis integrates with the full Plures ecosystem:
|
|
780
|
-
|
|
781
|
-
### PluresDB Integration
|
|
782
|
-
|
|
783
|
-
Local-first reactive datastore for offline-capable applications. Fully implemented with 32 tests covering all features.
|
|
784
|
-
|
|
785
|
-
```typescript
|
|
786
|
-
import {
|
|
787
|
-
createInMemoryDB,
|
|
788
|
-
createPraxisDBStore,
|
|
789
|
-
createPluresDBAdapter,
|
|
790
|
-
attachToEngine,
|
|
791
|
-
} from '@plures/praxis/pluresdb';
|
|
792
|
-
|
|
793
|
-
// Create an in-memory database
|
|
794
|
-
const db = createInMemoryDB();
|
|
795
|
-
|
|
796
|
-
// Create a PraxisDB store for facts and events
|
|
797
|
-
const store = createPraxisDBStore({ db });
|
|
798
|
-
|
|
799
|
-
// Or create an adapter to attach to an engine
|
|
800
|
-
const adapter = createPluresDBAdapter({
|
|
801
|
-
db,
|
|
802
|
-
registry,
|
|
803
|
-
initialContext: {},
|
|
804
|
-
});
|
|
805
|
-
|
|
806
|
-
// Attach adapter to engine for automatic persistence
|
|
807
|
-
adapter.attachEngine(engine);
|
|
808
|
-
|
|
809
|
-
// Persist facts and events
|
|
810
|
-
await adapter.persistFacts([{ tag: 'UserLoggedIn', payload: { userId: 'alice' } }]);
|
|
811
|
-
await adapter.persistEvents([{ tag: 'LOGIN', payload: { username: 'alice' } }]);
|
|
812
|
-
|
|
813
|
-
// Subscribe to changes
|
|
814
|
-
adapter.subscribeToEvents((events) => {
|
|
815
|
-
console.log('New events:', events);
|
|
816
|
-
});
|
|
817
|
-
```
|
|
818
|
-
|
|
819
|
-
**Features:**
|
|
820
|
-
|
|
821
|
-
- **In-memory adapter**: Ready-to-use implementation for development and testing
|
|
822
|
-
- **Reactive store**: Watch for changes with callbacks
|
|
823
|
-
- **Schema registry**: Store and retrieve schemas in PluresDB
|
|
824
|
-
- **Config generator**: Generate PluresDB configuration from Praxis schemas
|
|
825
|
-
- **Engine integration**: Automatic fact/event persistence
|
|
826
|
-
|
|
827
|
-
**Status**: ✅ Available (`src/core/pluresdb/`, `src/integrations/pluresdb.ts`)
|
|
828
|
-
**Tests**: 32 tests covering adapter, store, registry, and engine integration
|
|
829
|
-
|
|
830
|
-
### Unum Integration
|
|
831
|
-
|
|
832
|
-
Identity and channels for distributed systems. Fully implemented with comprehensive channel and identity management.
|
|
833
|
-
|
|
834
|
-
```typescript
|
|
835
|
-
import {
|
|
836
|
-
createUnumAdapter,
|
|
837
|
-
attachUnumToEngine,
|
|
838
|
-
} from '@plures/praxis';
|
|
839
|
-
|
|
840
|
-
// Create Unum adapter with identity
|
|
841
|
-
const unum = await createUnumAdapter({
|
|
842
|
-
db: pluresDB,
|
|
843
|
-
identity: {
|
|
844
|
-
name: 'my-app-node',
|
|
845
|
-
metadata: { role: 'coordinator' },
|
|
846
|
-
},
|
|
847
|
-
realtime: true,
|
|
848
|
-
});
|
|
849
|
-
|
|
850
|
-
// Create a channel for messaging
|
|
851
|
-
const channel = await unum.createChannel('app-events', ['member-1', 'member-2']);
|
|
852
|
-
|
|
853
|
-
// Broadcast Praxis events to channel
|
|
854
|
-
await unum.broadcastEvent(channel.id, {
|
|
855
|
-
tag: 'USER_JOINED',
|
|
856
|
-
payload: { userId: 'alice' },
|
|
857
|
-
});
|
|
858
|
-
|
|
859
|
-
// Subscribe to events from channel
|
|
860
|
-
const unsubscribe = unum.subscribeToEvents(channel.id, (event) => {
|
|
861
|
-
console.log('Received event:', event);
|
|
862
|
-
// Feed into local Praxis engine
|
|
863
|
-
engine.step([event]);
|
|
864
|
-
});
|
|
865
|
-
|
|
866
|
-
// Attach to engine for automatic event broadcasting
|
|
867
|
-
attachUnumToEngine(engine, unum, channel.id);
|
|
868
|
-
```
|
|
869
|
-
|
|
870
|
-
**Features:**
|
|
871
|
-
|
|
872
|
-
- **Identity Management**: Create and manage user/node identities
|
|
873
|
-
- **Channel Communication**: Real-time messaging between distributed nodes
|
|
874
|
-
- **Event Broadcasting**: Share Praxis events across channels
|
|
875
|
-
- **Fact Synchronization**: Distribute facts to connected participants
|
|
876
|
-
- **PluresDB Integration**: Persists identities and messages
|
|
877
|
-
|
|
878
|
-
**Status**: ✅ Available (`src/integrations/unum.ts`)
|
|
879
|
-
**Tests**: Comprehensive integration tests
|
|
880
|
-
**Use Cases**: Distributed messaging, identity management, multi-user collaboration
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
### State-Docs Integration
|
|
884
|
-
|
|
885
|
-
Living documentation generated from Praxis schemas. Fully implemented with Markdown and Mermaid diagram generation.
|
|
886
|
-
|
|
887
|
-
```typescript
|
|
888
|
-
import {
|
|
889
|
-
createStateDocsGenerator,
|
|
890
|
-
generateDocs,
|
|
891
|
-
} from '@plures/praxis';
|
|
892
|
-
|
|
893
|
-
// Create generator
|
|
894
|
-
const generator = createStateDocsGenerator({
|
|
895
|
-
projectTitle: 'My Praxis App',
|
|
896
|
-
target: './docs',
|
|
897
|
-
visualization: {
|
|
898
|
-
format: 'mermaid',
|
|
899
|
-
theme: 'default',
|
|
900
|
-
},
|
|
901
|
-
template: {
|
|
902
|
-
toc: true,
|
|
903
|
-
timestamp: true,
|
|
904
|
-
},
|
|
905
|
-
});
|
|
906
|
-
|
|
907
|
-
// Generate docs from schema
|
|
908
|
-
const docs = generator.generateFromSchema(appSchema);
|
|
909
|
-
|
|
910
|
-
// Or from registry
|
|
911
|
-
const registryDocs = generator.generateFromModule(myModule);
|
|
912
|
-
|
|
913
|
-
// Write generated docs
|
|
914
|
-
for (const doc of docs) {
|
|
915
|
-
await writeFile(doc.path, doc.content);
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
// Quick helper
|
|
919
|
-
const allDocs = generateDocs(appSchema, {
|
|
920
|
-
projectTitle: 'My App',
|
|
921
|
-
target: './docs',
|
|
922
|
-
});
|
|
923
|
-
```
|
|
924
|
-
|
|
925
|
-
**Features:**
|
|
926
|
-
|
|
927
|
-
- **Schema Documentation**: Auto-generate docs from Praxis schemas
|
|
928
|
-
- **Mermaid Diagrams**: Visual state machine and flow diagrams
|
|
929
|
-
- **Markdown Output**: GitHub-ready documentation
|
|
930
|
-
- **Model & Component Docs**: Detailed API documentation
|
|
931
|
-
- **Logic Flow Visualization**: Event → Rule → Fact diagrams
|
|
932
|
-
- **Table of Contents**: Automatic ToC generation
|
|
933
|
-
|
|
934
|
-
**Status**: ✅ Available (`src/integrations/state-docs.ts`)
|
|
935
|
-
**Documentation**: Auto-generates README, models.md, logic diagrams
|
|
936
|
-
|
|
937
|
-
### CodeCanvas Integration
|
|
938
|
-
|
|
939
|
-
Visual IDE for schema and logic editing. Fully implemented with schema visualization and canvas export.
|
|
940
|
-
|
|
941
|
-
```typescript
|
|
942
|
-
import {
|
|
943
|
-
schemaToCanvas,
|
|
944
|
-
canvasToSchema,
|
|
945
|
-
canvasToMermaid,
|
|
946
|
-
createCanvasEditor,
|
|
947
|
-
} from '@plures/praxis';
|
|
948
|
-
|
|
949
|
-
// Convert schema to canvas document
|
|
950
|
-
const canvas = schemaToCanvas(mySchema, {
|
|
951
|
-
layout: 'hierarchical',
|
|
952
|
-
});
|
|
953
|
-
|
|
954
|
-
// Export to YAML (Obsidian Canvas compatible)
|
|
955
|
-
const yaml = canvasToYaml(canvas);
|
|
956
|
-
await writeFile('./schema.canvas.yaml', yaml);
|
|
957
|
-
|
|
958
|
-
// Export to Mermaid diagram
|
|
959
|
-
const mermaid = canvasToMermaid(canvas);
|
|
960
|
-
|
|
961
|
-
// Create canvas editor instance
|
|
962
|
-
const editor = createCanvasEditor({
|
|
963
|
-
schema: mySchema,
|
|
964
|
-
enableFSM: true,
|
|
965
|
-
layout: 'hierarchical',
|
|
966
|
-
});
|
|
967
|
-
|
|
968
|
-
// Add nodes programmatically
|
|
969
|
-
editor.addNode({
|
|
970
|
-
type: 'model',
|
|
971
|
-
label: 'User',
|
|
972
|
-
x: 100,
|
|
973
|
-
y: 100,
|
|
974
|
-
width: 150,
|
|
975
|
-
height: 60,
|
|
976
|
-
data: userModel,
|
|
977
|
-
});
|
|
978
|
-
|
|
979
|
-
// Convert back to schema
|
|
980
|
-
const updatedSchema = editor.toSchema();
|
|
981
|
-
```
|
|
982
|
-
|
|
983
|
-
**Features:**
|
|
984
|
-
|
|
985
|
-
- **Visual Schema Design**: Node-based schema editor
|
|
986
|
-
- **Canvas Export**: YAML and Mermaid diagram formats
|
|
987
|
-
- **Obsidian Compatible**: Works with Obsidian Canvas format
|
|
988
|
-
- **FSM Visualization**: State machine and flow diagrams
|
|
989
|
-
- **Bi-directional Sync**: Canvas ↔ Schema round-tripping
|
|
990
|
-
- **Guardian Validation**: Pre-commit lifecycle checks
|
|
991
|
-
|
|
992
|
-
**Status**: ✅ Available (`src/integrations/code-canvas.ts`)
|
|
993
|
-
**Documentation**: [docs/guides/canvas.md](./docs/guides/canvas.md)
|
|
994
|
-
|
|
995
|
-
### Svelte Integration
|
|
996
|
-
|
|
997
|
-
Svelte v5 integration with reactive stores.
|
|
998
|
-
|
|
999
|
-
```typescript
|
|
1000
|
-
import { createPraxisStore } from '@plures/praxis/svelte';
|
|
1001
|
-
|
|
1002
|
-
const stateStore = createPraxisStore(engine);
|
|
1003
|
-
const userStore = createDerivedStore(engine, (ctx) => ctx.currentUser);
|
|
1004
|
-
|
|
1005
|
-
// In Svelte component:
|
|
1006
|
-
// $: currentUser = $userStore;
|
|
1007
|
-
```
|
|
1008
|
-
|
|
1009
|
-
## Cross-Language Usage
|
|
1010
|
-
|
|
1011
|
-
### PowerShell
|
|
1012
|
-
|
|
1013
|
-
Full PowerShell adapter for using Praxis from PowerShell scripts:
|
|
1014
|
-
|
|
1015
|
-
```powershell
|
|
1016
|
-
# Import module
|
|
1017
|
-
Import-Module ./powershell/Praxis.psm1
|
|
1018
|
-
|
|
1019
|
-
# Initialize adapter
|
|
1020
|
-
Initialize-PraxisAdapter -EnginePath "./dist/adapters/cli.js"
|
|
1021
|
-
|
|
1022
|
-
# Create state and events
|
|
1023
|
-
$state = New-PraxisState -Context @{ count = 0 }
|
|
1024
|
-
$event = New-PraxisEvent -Tag "INCREMENT" -Payload @{}
|
|
1025
|
-
|
|
1026
|
-
# Process step
|
|
1027
|
-
$result = Invoke-PraxisStep -State $state -Events @($event) -ConfigPath "./config.json"
|
|
1028
|
-
|
|
1029
|
-
# Use result
|
|
1030
|
-
Write-Host "Count: $($result.state.context.count)"
|
|
1031
|
-
```
|
|
1032
|
-
|
|
1033
|
-
See [powershell/README.md](./powershell/README.md) for complete documentation and examples.
|
|
1034
|
-
|
|
1035
|
-
### C# (.NET 8+)
|
|
1036
|
-
|
|
1037
|
-
Full C# implementation with functional, immutable design:
|
|
1038
|
-
|
|
1039
|
-
```csharp
|
|
1040
|
-
using Praxis.Core;
|
|
1041
|
-
using Praxis.Dsl;
|
|
1042
|
-
|
|
1043
|
-
// Define facts and events
|
|
1044
|
-
var UserLoggedIn = PraxisDsl.DefineFact<UserPayload>("UserLoggedIn");
|
|
1045
|
-
var Login = PraxisDsl.DefineEvent<LoginPayload>("LOGIN");
|
|
1046
|
-
|
|
1047
|
-
record UserPayload(string UserId);
|
|
1048
|
-
record LoginPayload(string Username);
|
|
1049
|
-
|
|
1050
|
-
// Define rules
|
|
1051
|
-
var loginRule = PraxisDsl.DefineRule<AuthContext>(
|
|
1052
|
-
id: "auth.login",
|
|
1053
|
-
description: "Process login event",
|
|
1054
|
-
impl: (state, context, events) =>
|
|
1055
|
-
{
|
|
1056
|
-
var loginEvent = events.FindEvent(Login);
|
|
1057
|
-
if (loginEvent != null)
|
|
1058
|
-
{
|
|
1059
|
-
var payload = Login.GetPayload(loginEvent);
|
|
1060
|
-
return [UserLoggedIn.Create(new UserPayload(payload?.Username ?? "unknown"))];
|
|
1061
|
-
}
|
|
1062
|
-
return [];
|
|
1063
|
-
});
|
|
1064
|
-
|
|
1065
|
-
// Create engine
|
|
1066
|
-
var registry = new PraxisRegistry<AuthContext>();
|
|
1067
|
-
registry.RegisterRule(loginRule);
|
|
1068
|
-
|
|
1069
|
-
var engine = PraxisEngine.Create(new PraxisEngineOptions<AuthContext>
|
|
1070
|
-
{
|
|
1071
|
-
InitialContext = new AuthContext(null),
|
|
1072
|
-
Registry = registry
|
|
1073
|
-
});
|
|
1074
|
-
|
|
1075
|
-
// Dispatch events
|
|
1076
|
-
var result = engine.Step([Login.Create(new LoginPayload("alice"))]);
|
|
1077
|
-
Console.WriteLine($"Facts: {result.State.Facts.Count}"); // Facts: 1
|
|
1078
|
-
```
|
|
1079
|
-
|
|
1080
|
-
See [csharp/Praxis/README.md](./csharp/Praxis/README.md) for complete documentation.
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
## Cross-Language Support
|
|
1084
|
-
|
|
1085
|
-
The core protocol is implemented across multiple languages:
|
|
1086
|
-
|
|
1087
|
-
**TypeScript** (Primary, npm: `@plures/praxis`)
|
|
1088
|
-
|
|
1089
|
-
```typescript
|
|
1090
|
-
import { createPraxisEngine, PraxisRegistry } from '@plures/praxis';
|
|
1091
|
-
|
|
1092
|
-
const engine = createPraxisEngine({
|
|
1093
|
-
initialContext: {},
|
|
1094
|
-
registry: new PraxisRegistry(),
|
|
1095
|
-
});
|
|
1096
|
-
const result = engine.step(events);
|
|
1097
|
-
```
|
|
1098
|
-
|
|
1099
|
-
**C#** (.NET 8+, NuGet: `Plures.Praxis`)
|
|
1100
|
-
|
|
1101
|
-
```csharp
|
|
1102
|
-
var engine = PraxisEngine.Create(new PraxisEngineOptions<TContext> { ... });
|
|
1103
|
-
var result = engine.Step(events);
|
|
1104
|
-
```
|
|
1105
|
-
|
|
1106
|
-
See [csharp/Praxis/README.md](./csharp/Praxis/README.md) for full documentation.
|
|
1107
|
-
|
|
1108
|
-
**PowerShell** (GitHub: `Praxis.psm1`)
|
|
1109
|
-
|
|
1110
|
-
```powershell
|
|
1111
|
-
$newState = Invoke-PraxisStep -State $state -Events $events
|
|
1112
|
-
```
|
|
1113
|
-
|
|
1114
|
-
See [powershell/README.md](./powershell/README.md) for full documentation.
|
|
252
|
+
## Documentation
|
|
1115
253
|
|
|
1116
|
-
|
|
1117
|
-
|
|
254
|
+
| Resource | Link |
|
|
255
|
+
|---|---|
|
|
256
|
+
| Getting Started | [GETTING_STARTED.md](./GETTING_STARTED.md) |
|
|
257
|
+
| Framework Architecture | [FRAMEWORK.md](./FRAMEWORK.md) |
|
|
258
|
+
| Core API Reference | [docs/core/praxis-core-api.md](./docs/core/praxis-core-api.md) |
|
|
259
|
+
| Extending Praxis | [docs/core/extending-praxis-core.md](./docs/core/extending-praxis-core.md) |
|
|
260
|
+
| Svelte Integration | [docs/guides/svelte-integration.md](./docs/guides/svelte-integration.md) |
|
|
261
|
+
| CodeCanvas Guide | [docs/guides/canvas.md](./docs/guides/canvas.md) |
|
|
262
|
+
| Decision Ledger | [docs/decision-ledger/DOGFOODING.md](./docs/decision-ledger/DOGFOODING.md) |
|
|
263
|
+
| Tutorials | [docs/tutorials/](./docs/tutorials/) |
|
|
264
|
+
| Migration from 1.x | [MIGRATION_GUIDE.md](./MIGRATION_GUIDE.md) |
|
|
265
|
+
| Roadmap | [ROADMAP.md](./ROADMAP.md) |
|
|
266
|
+
| 1.x Archive | [docs/archive/1.x/](./docs/archive/1.x/) |
|
|
1118
267
|
|
|
1119
268
|
## Development
|
|
1120
269
|
|
|
1121
270
|
```bash
|
|
1122
|
-
# Install dependencies
|
|
1123
|
-
npm
|
|
1124
|
-
|
|
1125
|
-
#
|
|
1126
|
-
npm run build
|
|
1127
|
-
|
|
1128
|
-
# Run tests
|
|
1129
|
-
npm test
|
|
1130
|
-
|
|
1131
|
-
# Type check
|
|
1132
|
-
npm run typecheck
|
|
1133
|
-
```
|
|
1134
|
-
|
|
1135
|
-
### Deno Development
|
|
1136
|
-
|
|
1137
|
-
```bash
|
|
1138
|
-
# Run with Deno
|
|
1139
|
-
deno task dev
|
|
1140
|
-
|
|
1141
|
-
# Run tests
|
|
1142
|
-
deno task test
|
|
1143
|
-
|
|
1144
|
-
# Lint and format
|
|
1145
|
-
deno task lint
|
|
1146
|
-
deno task fmt
|
|
271
|
+
npm install # Install dependencies
|
|
272
|
+
npm run build # Build (ESM + CJS via tsup)
|
|
273
|
+
npm test # Run tests (Vitest)
|
|
274
|
+
npm run typecheck # Type-check
|
|
1147
275
|
```
|
|
1148
276
|
|
|
1149
|
-
For more detailed development information, see [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
1150
|
-
|
|
1151
|
-
## License
|
|
1152
|
-
|
|
1153
|
-
MIT License - see [LICENSE](./LICENSE) for details.
|
|
1154
|
-
|
|
1155
277
|
## Contributing
|
|
1156
278
|
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
**Automated Updates**: This repository uses batched bot updates to reduce commit churn. Dependency updates are grouped weekly and include audit trails. See the [Bot Update Policy](./docs/BOT_UPDATE_POLICY.md) for details.
|
|
1160
|
-
|
|
1161
|
-
**Dogfooding Plures Tools**: We actively dogfood all Plures tools during development. If you encounter friction while using any tool, please file a [Dogfooding Friction Report](https://github.com/plures/praxis/issues/new/choose). See the [Dogfooding Quick Start](./docs/DOGFOODING_QUICK_START.md) for details.
|
|
279
|
+
PRs and discussions welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
|
|
1162
280
|
|
|
1163
281
|
- 🐛 [Report a bug](https://github.com/plures/praxis/issues/new?template=bug_report.yml)
|
|
1164
282
|
- 💡 [Request a feature](https://github.com/plures/praxis/issues/new?template=enhancement.yml)
|
|
1165
|
-
- 📖 [Improve documentation](https://github.com/plures/praxis/issues/new?template=bug_report.yml)
|
|
1166
283
|
- 🔒 [Report a security issue](./SECURITY.md)
|
|
1167
284
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
## Support
|
|
1171
|
-
|
|
1172
|
-
- 📚 [Documentation](./docs/)
|
|
1173
|
-
- 💬 [GitHub Discussions](https://github.com/plures/praxis/discussions)
|
|
1174
|
-
- 🐛 [Issue Tracker](https://github.com/plures/praxis/issues)
|
|
1175
|
-
- 🌐 [Plures Organization](https://github.com/plures)
|
|
1176
|
-
|
|
1177
|
-
---
|
|
1178
|
-
|
|
1179
|
-
**Praxis** – Because application logic should be practical, provable, and portable.
|
|
1180
|
-
|
|
1181
|
-
---
|
|
285
|
+
## License
|
|
1182
286
|
|
|
1183
|
-
|
|
287
|
+
[MIT](./LICENSE)
|
|
1184
288
|
|
|
1185
289
|
---
|
|
1186
290
|
|
|
1187
|
-
|
|
1188
|
-
## Overview
|
|
1189
|
-
|
|
1190
|
-
## Install
|
|
291
|
+
**Praxis** — practical, provable, portable application logic.
|
|
1191
292
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
## Contributing
|
|
1195
|
-
|
|
1196
|
-
## License
|
|
293
|
+
Built with ❤️ by the [Plures](https://github.com/plures) team
|