@plures/praxis 1.2.12 → 1.2.41
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 +63 -0
- package/dist/browser/{chunk-VOMLVI6V.js → chunk-BBP2F7TT.js} +70 -1
- package/dist/browser/{chunk-K377RW4V.js → chunk-FCEH7WMH.js} +1 -1
- package/dist/browser/{engine-YJZV4SLD.js → engine-65QDGCAN.js} +1 -1
- package/dist/browser/index.d.ts +104 -2
- package/dist/browser/index.js +181 -5
- package/dist/browser/integrations/svelte.d.ts +2 -2
- package/dist/browser/integrations/svelte.js +2 -2
- package/dist/browser/{reactive-engine.svelte-9aS0kTa8.d.ts → reactive-engine.svelte-Cqd8Mod2.d.ts} +56 -1
- package/dist/node/{chunk-PRPQO6R5.js → chunk-32YFEEML.js} +1 -1
- package/dist/node/{chunk-VOMLVI6V.js → chunk-BBP2F7TT.js} +70 -1
- package/dist/node/{chunk-5RH7UAQC.js → chunk-PTH6MD6P.js} +1 -0
- package/dist/node/cli/index.cjs +1553 -839
- package/dist/node/cli/index.js +39 -2
- package/dist/node/cloud/index.d.cts +1 -1
- package/dist/node/cloud/index.d.ts +1 -1
- package/dist/node/components/index.d.cts +2 -2
- package/dist/node/components/index.d.ts +2 -2
- package/dist/node/conversations-KQBXTP3N.js +596 -0
- package/dist/node/{engine-2DQBKBJC.js → engine-7CXQV6RC.js} +1 -1
- package/dist/node/index.cjs +408 -3
- package/dist/node/index.d.cts +308 -7
- package/dist/node/index.d.ts +308 -7
- package/dist/node/index.js +336 -6
- package/dist/node/integrations/svelte.cjs +70 -1
- package/dist/node/integrations/svelte.d.cts +3 -3
- package/dist/node/integrations/svelte.d.ts +3 -3
- package/dist/node/integrations/svelte.js +2 -2
- package/dist/node/{protocol-Qek7ebBl.d.ts → protocol-BocKczNv.d.cts} +1 -1
- package/dist/node/{protocol-Qek7ebBl.d.cts → protocol-BocKczNv.d.ts} +1 -1
- package/dist/node/{reactive-engine.svelte-CRNqHlbv.d.ts → reactive-engine.svelte-CGe8SpVE.d.cts} +57 -2
- package/dist/node/{reactive-engine.svelte-BFIZfawz.d.cts → reactive-engine.svelte-D-xTDxT5.d.ts} +57 -2
- package/dist/node/{terminal-adapter-B-UK_Vdz.d.ts → terminal-adapter-CvIvgTo4.d.ts} +1 -1
- package/dist/node/{terminal-adapter-BQSIF5bf.d.cts → terminal-adapter-Db-snPJ3.d.cts} +1 -1
- package/dist/node/{validate-CNHUULQE.js → validate-EN3M4FUR.js} +1 -1
- package/dist/node/{verify-KLJRXVJS.js → verify-7VZRP2WS.js} +2 -2
- package/docs/BOT_UPDATE_POLICY.md +125 -0
- package/docs/DOGFOODING_CHECKLIST.md +254 -0
- package/docs/DOGFOODING_INDEX.md +169 -0
- package/docs/DOGFOODING_QUICK_START.md +140 -0
- package/docs/KNO_ENG_EXTRACTION_PLAN.md +577 -0
- package/docs/PLURES_TOOLS_INVENTORY.md +170 -0
- package/docs/README.md +12 -0
- package/docs/TESTING_BOT_WORKFLOWS.md +154 -0
- package/docs/conversations/INTEGRATION_POINTS.md +719 -0
- package/docs/conversations/README.md +168 -0
- package/docs/core/extending-praxis-core.md +604 -0
- package/docs/core/praxis-core-api.md +385 -0
- package/docs/decision-ledger/contract-index.json +2 -2
- package/docs/decision-ledger/decisions/2026-02-01-monorepo-organization.md +130 -0
- package/docs/examples/DOGFOODING_WORKFLOW_EXAMPLE.md +295 -0
- package/docs/examples/README.md +41 -0
- package/docs/workflows/pr-overlap-guard.md +50 -0
- package/package.json +7 -2
- package/src/__tests__/chronicle.test.ts +512 -0
- package/src/__tests__/conversations.test.ts +312 -0
- package/src/__tests__/edge-cases.test.ts +1 -1
- package/src/__tests__/engine-dx.test.ts +355 -0
- package/src/cli/commands/conversations.ts +252 -0
- package/src/cli/index.ts +73 -0
- package/src/conversations/README.md +230 -0
- package/src/conversations/candidate.schema.json +123 -0
- package/src/conversations/candidates.ts +114 -0
- package/src/conversations/capture.ts +56 -0
- package/src/conversations/classify.ts +110 -0
- package/src/conversations/conversation.schema.json +106 -0
- package/src/conversations/emitters/fs.ts +65 -0
- package/src/conversations/emitters/github.ts +115 -0
- package/src/conversations/gate.ts +102 -0
- package/src/conversations/index.ts +28 -0
- package/src/conversations/normalize.ts +51 -0
- package/src/conversations/redact.ts +57 -0
- package/src/conversations/types.ts +96 -0
- package/src/core/chronicle/chronicle.ts +227 -0
- package/src/core/chronicle/context.ts +80 -0
- package/src/core/chronicle/index.ts +53 -0
- package/src/core/chronicle/mcp.ts +135 -0
- package/src/core/chronicle/types.ts +61 -0
- package/src/core/engine.ts +99 -1
- package/src/core/pluresdb/index.ts +22 -0
- package/src/core/pluresdb/store.ts +162 -5
- package/src/core/rules.ts +12 -0
- package/src/dsl/index.ts +6 -0
- package/src/index.ts +18 -0
- package/src/integrations/pluresdb.ts +22 -0
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Praxis
|
|
2
2
|
|
|
3
|
+
<!-- plures-readme-banner -->
|
|
4
|
+
[](https://github.com/plures/praxis/actions/workflows/ci.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/@plures/praxis)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
|
|
3
9
|
**Typed, visual-first application logic for Svelte, Node, and the browser.**
|
|
4
10
|
|
|
5
11
|
[](https://www.npmjs.com/package/@plures/praxis)
|
|
@@ -312,6 +318,8 @@ See [src/decision-ledger/README.md](./src/decision-ledger/README.md) for complet
|
|
|
312
318
|
## Documentation
|
|
313
319
|
- [Getting Started](./GETTING_STARTED.md)
|
|
314
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
|
|
315
323
|
- [Decision Ledger Guide](./src/decision-ledger/README.md)
|
|
316
324
|
- [Examples](./examples/)
|
|
317
325
|
|
|
@@ -452,6 +460,44 @@ This protocol is:
|
|
|
452
460
|
|
|
453
461
|
## Framework Architecture
|
|
454
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)
|
|
498
|
+
|
|
499
|
+
The existing code is currently located in:
|
|
500
|
+
|
|
455
501
|
```
|
|
456
502
|
/praxis
|
|
457
503
|
├── core/ # Core framework
|
|
@@ -1110,6 +1156,10 @@ MIT License - see [LICENSE](./LICENSE) for details.
|
|
|
1110
1156
|
|
|
1111
1157
|
Contributions are welcome! Please read our [Contributing Guide](./CONTRIBUTING.md) to get started.
|
|
1112
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.
|
|
1162
|
+
|
|
1113
1163
|
- 🐛 [Report a bug](https://github.com/plures/praxis/issues/new?template=bug_report.yml)
|
|
1114
1164
|
- 💡 [Request a feature](https://github.com/plures/praxis/issues/new?template=enhancement.yml)
|
|
1115
1165
|
- 📖 [Improve documentation](https://github.com/plures/praxis/issues/new?template=bug_report.yml)
|
|
@@ -1131,3 +1181,16 @@ Please review our [Code of Conduct](./CODE_OF_CONDUCT.md) before participating.
|
|
|
1131
1181
|
---
|
|
1132
1182
|
|
|
1133
1183
|
Built with ❤️ by the plures team
|
|
1184
|
+
|
|
1185
|
+
---
|
|
1186
|
+
|
|
1187
|
+
<!-- plures-readme-standard-sections -->
|
|
1188
|
+
## Overview
|
|
1189
|
+
|
|
1190
|
+
## Install
|
|
1191
|
+
|
|
1192
|
+
## Development
|
|
1193
|
+
|
|
1194
|
+
## Contributing
|
|
1195
|
+
|
|
1196
|
+
## License
|
|
@@ -20,8 +20,12 @@ function safeClone(value) {
|
|
|
20
20
|
var LogicEngine = class {
|
|
21
21
|
state;
|
|
22
22
|
registry;
|
|
23
|
+
factDedup;
|
|
24
|
+
maxFacts;
|
|
23
25
|
constructor(options) {
|
|
24
26
|
this.registry = options.registry;
|
|
27
|
+
this.factDedup = options.factDedup ?? "last-write-wins";
|
|
28
|
+
this.maxFacts = options.maxFacts ?? 1e3;
|
|
25
29
|
this.state = {
|
|
26
30
|
context: options.initialContext,
|
|
27
31
|
facts: options.initialFacts ?? [],
|
|
@@ -77,6 +81,7 @@ var LogicEngine = class {
|
|
|
77
81
|
const diagnostics = [];
|
|
78
82
|
let newState = { ...this.state };
|
|
79
83
|
const newFacts = [];
|
|
84
|
+
const eventTags = new Set(events.map((e) => e.tag));
|
|
80
85
|
for (const ruleId of config.ruleIds) {
|
|
81
86
|
const rule = this.registry.getRule(ruleId);
|
|
82
87
|
if (!rule) {
|
|
@@ -87,6 +92,12 @@ var LogicEngine = class {
|
|
|
87
92
|
});
|
|
88
93
|
continue;
|
|
89
94
|
}
|
|
95
|
+
if (rule.eventTypes) {
|
|
96
|
+
const filterTags = Array.isArray(rule.eventTypes) ? rule.eventTypes : [rule.eventTypes];
|
|
97
|
+
if (!filterTags.some((t) => eventTags.has(t))) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
90
101
|
try {
|
|
91
102
|
const ruleFacts = rule.impl(newState, events);
|
|
92
103
|
newFacts.push(...ruleFacts);
|
|
@@ -98,9 +109,29 @@ var LogicEngine = class {
|
|
|
98
109
|
});
|
|
99
110
|
}
|
|
100
111
|
}
|
|
112
|
+
let mergedFacts;
|
|
113
|
+
switch (this.factDedup) {
|
|
114
|
+
case "last-write-wins": {
|
|
115
|
+
const factMap = /* @__PURE__ */ new Map();
|
|
116
|
+
for (const f of newState.facts) factMap.set(f.tag, f);
|
|
117
|
+
for (const f of newFacts) factMap.set(f.tag, f);
|
|
118
|
+
mergedFacts = Array.from(factMap.values());
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case "append":
|
|
122
|
+
mergedFacts = [...newState.facts, ...newFacts];
|
|
123
|
+
break;
|
|
124
|
+
case "none":
|
|
125
|
+
default:
|
|
126
|
+
mergedFacts = [...newState.facts, ...newFacts];
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
if (this.maxFacts > 0 && mergedFacts.length > this.maxFacts) {
|
|
130
|
+
mergedFacts = mergedFacts.slice(mergedFacts.length - this.maxFacts);
|
|
131
|
+
}
|
|
101
132
|
newState = {
|
|
102
133
|
...newState,
|
|
103
|
-
facts:
|
|
134
|
+
facts: mergedFacts
|
|
104
135
|
};
|
|
105
136
|
for (const constraintId of config.constraintIds) {
|
|
106
137
|
const constraint = this.registry.getConstraint(constraintId);
|
|
@@ -153,6 +184,29 @@ var LogicEngine = class {
|
|
|
153
184
|
context: updater(this.state.context)
|
|
154
185
|
};
|
|
155
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Atomically update context AND process events in a single call.
|
|
189
|
+
*
|
|
190
|
+
* This avoids the fragile pattern of calling updateContext() then step()
|
|
191
|
+
* separately, where rules could see stale context if the ordering is wrong.
|
|
192
|
+
*
|
|
193
|
+
* @param updater Function that produces new context from old context
|
|
194
|
+
* @param events Events to process after context is updated
|
|
195
|
+
* @returns Result with new state and diagnostics
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* engine.stepWithContext(
|
|
199
|
+
* ctx => ({ ...ctx, sprintName: sprint.name, items: sprint.items }),
|
|
200
|
+
* [{ tag: 'sprint.update', payload: { name: sprint.name } }]
|
|
201
|
+
* );
|
|
202
|
+
*/
|
|
203
|
+
stepWithContext(updater, events) {
|
|
204
|
+
this.state = {
|
|
205
|
+
...this.state,
|
|
206
|
+
context: updater(this.state.context)
|
|
207
|
+
};
|
|
208
|
+
return this.step(events);
|
|
209
|
+
}
|
|
156
210
|
/**
|
|
157
211
|
* Add facts directly (for exceptional cases).
|
|
158
212
|
* Generally, facts should be added through rules.
|
|
@@ -165,6 +219,21 @@ var LogicEngine = class {
|
|
|
165
219
|
facts: [...this.state.facts, ...facts]
|
|
166
220
|
};
|
|
167
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Check all constraints without processing any events.
|
|
224
|
+
*
|
|
225
|
+
* Useful for validation-only scenarios (e.g., form validation,
|
|
226
|
+
* pre-save checks) where you want constraint diagnostics without
|
|
227
|
+
* triggering any rules.
|
|
228
|
+
*
|
|
229
|
+
* @returns Array of constraint violation diagnostics (empty = all passing)
|
|
230
|
+
*/
|
|
231
|
+
checkConstraints() {
|
|
232
|
+
return this.stepWithConfig([], {
|
|
233
|
+
ruleIds: [],
|
|
234
|
+
constraintIds: this.registry.getConstraintIds()
|
|
235
|
+
}).diagnostics;
|
|
236
|
+
}
|
|
168
237
|
/**
|
|
169
238
|
* Clear all facts
|
|
170
239
|
*/
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { L as LogicEngine, P as PraxisState, a as PraxisEvent, b as PraxisRegistry, R as RuleDescriptor, C as ConstraintDescriptor, c as
|
|
2
|
-
export {
|
|
1
|
+
import { L as LogicEngine, P as PraxisState, a as PraxisEvent, b as PraxisRegistry, R as RuleDescriptor, C as ConstraintDescriptor, c as ConstraintFn, d as Contract, e as RuleFn, f as PraxisFact, g as PraxisModule } from './reactive-engine.svelte-Cqd8Mod2.js';
|
|
2
|
+
export { h as ConstraintId, i as PRAXIS_PROTOCOL_VERSION, j as PraxisDiagnostics, k as PraxisEngineOptions, l as PraxisStepConfig, m as PraxisStepFn, n as PraxisStepResult, o as ReactiveEngineOptions, p as ReactiveLogicEngine, q as RuleId, r as createPraxisEngine, s as createReactiveEngine } from './reactive-engine.svelte-Cqd8Mod2.js';
|
|
3
3
|
import { LocalFirstOptions } from '@plures/pluresdb/local-first';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -435,6 +435,11 @@ interface DefineRuleOptions<TContext = unknown> {
|
|
|
435
435
|
id: string;
|
|
436
436
|
description: string;
|
|
437
437
|
impl: RuleFn<TContext>;
|
|
438
|
+
/**
|
|
439
|
+
* Optional event type filter — only evaluate this rule when at least one
|
|
440
|
+
* event in the batch has a matching `tag`. Accepts a single tag or array.
|
|
441
|
+
*/
|
|
442
|
+
eventTypes?: string | string[];
|
|
438
443
|
contract?: Contract;
|
|
439
444
|
meta?: Record<string, unknown>;
|
|
440
445
|
}
|
|
@@ -1103,6 +1108,86 @@ interface PraxisLocalFirstOptions extends LocalFirstOptions {
|
|
|
1103
1108
|
*/
|
|
1104
1109
|
declare function createPraxisLocalFirst(options?: PraxisLocalFirstOptions): Promise<PluresDBPraxisAdapter>;
|
|
1105
1110
|
|
|
1111
|
+
/**
|
|
1112
|
+
* Chronicle Types
|
|
1113
|
+
*
|
|
1114
|
+
* Core types for the Chronicle causal tracking system.
|
|
1115
|
+
* Records state transitions as a causal graph stored in PluresDB.
|
|
1116
|
+
*/
|
|
1117
|
+
/**
|
|
1118
|
+
* Direction for traversing causal chains.
|
|
1119
|
+
*/
|
|
1120
|
+
type TraceDirection = 'backward' | 'forward' | 'both';
|
|
1121
|
+
/**
|
|
1122
|
+
* A recorded state transition event passed to Chronicle.
|
|
1123
|
+
*/
|
|
1124
|
+
interface ChronicleEvent {
|
|
1125
|
+
/** Path to the changed value (fact or event stream path) */
|
|
1126
|
+
path: string;
|
|
1127
|
+
/** Value before the change (undefined for creates) */
|
|
1128
|
+
before?: unknown;
|
|
1129
|
+
/** Value after the change */
|
|
1130
|
+
after?: unknown;
|
|
1131
|
+
/** Parent span/node ID that caused this change */
|
|
1132
|
+
cause?: string;
|
|
1133
|
+
/** Session or request ID grouping related changes */
|
|
1134
|
+
context?: string;
|
|
1135
|
+
/** Additional metadata key-value pairs */
|
|
1136
|
+
metadata: Record<string, string>;
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* A Chronicle node representing a single recorded state transition.
|
|
1140
|
+
*/
|
|
1141
|
+
interface ChronicleNode {
|
|
1142
|
+
/** Unique node ID: `chronos:{timestamp}-{counter}` */
|
|
1143
|
+
id: string;
|
|
1144
|
+
/** Timestamp (ms since epoch) when this node was recorded */
|
|
1145
|
+
timestamp: number;
|
|
1146
|
+
/** The recorded state transition */
|
|
1147
|
+
event: ChronicleEvent;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* Chronicle Interface and PluresDbChronicle Implementation
|
|
1152
|
+
*
|
|
1153
|
+
* Records state transitions as a causal graph in PluresDB.
|
|
1154
|
+
* Attached to PraxisDBStore via `.withChronicle()` for zero-effort observability.
|
|
1155
|
+
*/
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* Chronicle interface — records state transitions as a causal graph.
|
|
1159
|
+
*
|
|
1160
|
+
* Automatically attached to any PraxisDBStore at runtime via `.withChronicle()`.
|
|
1161
|
+
* Records state diffs as graph nodes with causal edges.
|
|
1162
|
+
*/
|
|
1163
|
+
interface Chronicle {
|
|
1164
|
+
/**
|
|
1165
|
+
* Record a state transition and return the created node.
|
|
1166
|
+
*/
|
|
1167
|
+
record(event: ChronicleEvent): Promise<ChronicleNode>;
|
|
1168
|
+
/**
|
|
1169
|
+
* Trace causality backward or forward from a node.
|
|
1170
|
+
*
|
|
1171
|
+
* @param nodeId Starting node ID
|
|
1172
|
+
* @param direction `'backward'` follows incoming edges, `'forward'` follows outgoing edges
|
|
1173
|
+
* @param maxDepth Maximum traversal depth (prevents cycles / infinite loops)
|
|
1174
|
+
*/
|
|
1175
|
+
trace(nodeId: string, direction: TraceDirection, maxDepth: number): Promise<ChronicleNode[]>;
|
|
1176
|
+
/**
|
|
1177
|
+
* Return all Chronicle nodes recorded within a timestamp range.
|
|
1178
|
+
*
|
|
1179
|
+
* @param start Inclusive start timestamp (ms)
|
|
1180
|
+
* @param end Inclusive end timestamp (ms)
|
|
1181
|
+
*/
|
|
1182
|
+
range(start: number, end: number): Promise<ChronicleNode[]>;
|
|
1183
|
+
/**
|
|
1184
|
+
* Return all Chronicle nodes belonging to a context (session/request).
|
|
1185
|
+
*
|
|
1186
|
+
* @param contextId The context identifier
|
|
1187
|
+
*/
|
|
1188
|
+
subgraph(contextId: string): Promise<ChronicleNode[]>;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1106
1191
|
/**
|
|
1107
1192
|
* PraxisDB Store
|
|
1108
1193
|
*
|
|
@@ -1182,9 +1267,26 @@ declare class PraxisDBStore<TContext = unknown> {
|
|
|
1182
1267
|
private subscriptions;
|
|
1183
1268
|
private factWatchers;
|
|
1184
1269
|
private onRuleError;
|
|
1270
|
+
private chronicle?;
|
|
1185
1271
|
constructor(options: PraxisDBStoreOptions<TContext> & {
|
|
1186
1272
|
onRuleError?: RuleErrorHandler;
|
|
1187
1273
|
});
|
|
1274
|
+
/**
|
|
1275
|
+
* Attach a Chronicle observer to this store.
|
|
1276
|
+
*
|
|
1277
|
+
* Every subsequent `storeFact` and `appendEvent` call will be recorded as a
|
|
1278
|
+
* causal graph node in PluresDB, enabling full observability for free.
|
|
1279
|
+
*
|
|
1280
|
+
* @param chronicle Chronicle implementation to attach
|
|
1281
|
+
* @returns `this` for fluent chaining
|
|
1282
|
+
*
|
|
1283
|
+
* @example
|
|
1284
|
+
* ```typescript
|
|
1285
|
+
* const store = createPraxisDBStore(db, registry)
|
|
1286
|
+
* .withChronicle(createChronicle(db));
|
|
1287
|
+
* ```
|
|
1288
|
+
*/
|
|
1289
|
+
withChronicle(chronicle: Chronicle): this;
|
|
1188
1290
|
/**
|
|
1189
1291
|
* Store a fact in PluresDB
|
|
1190
1292
|
*
|
package/dist/browser/index.js
CHANGED
|
@@ -2,12 +2,12 @@ import {
|
|
|
2
2
|
PraxisRegistry,
|
|
3
3
|
ReactiveLogicEngine,
|
|
4
4
|
createReactiveEngine
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-FCEH7WMH.js";
|
|
6
6
|
import {
|
|
7
7
|
LogicEngine,
|
|
8
8
|
PRAXIS_PROTOCOL_VERSION,
|
|
9
9
|
createPraxisEngine
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-BBP2F7TT.js";
|
|
11
11
|
import {
|
|
12
12
|
InMemoryPraxisDB,
|
|
13
13
|
PluresDBPraxisAdapter,
|
|
@@ -586,6 +586,7 @@ function defineRule(options) {
|
|
|
586
586
|
id: options.id,
|
|
587
587
|
description: options.description,
|
|
588
588
|
impl: options.impl,
|
|
589
|
+
eventTypes: options.eventTypes,
|
|
589
590
|
contract,
|
|
590
591
|
meta
|
|
591
592
|
};
|
|
@@ -884,6 +885,53 @@ function validateForGeneration(schema) {
|
|
|
884
885
|
};
|
|
885
886
|
}
|
|
886
887
|
|
|
888
|
+
// src/core/chronicle/context.ts
|
|
889
|
+
var ChronicleContext = class {
|
|
890
|
+
static _stack = [];
|
|
891
|
+
/**
|
|
892
|
+
* Get the current active span, if any.
|
|
893
|
+
*/
|
|
894
|
+
static get current() {
|
|
895
|
+
return this._stack[this._stack.length - 1];
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Run a synchronous function within a causal span.
|
|
899
|
+
* The span is automatically popped when the function returns.
|
|
900
|
+
*/
|
|
901
|
+
static run(span, fn) {
|
|
902
|
+
this._stack.push(span);
|
|
903
|
+
try {
|
|
904
|
+
return fn();
|
|
905
|
+
} finally {
|
|
906
|
+
this._stack.pop();
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Run an async function within a causal span.
|
|
911
|
+
* The span is popped after the promise settles.
|
|
912
|
+
*/
|
|
913
|
+
static async runAsync(span, fn) {
|
|
914
|
+
this._stack.push(span);
|
|
915
|
+
try {
|
|
916
|
+
return await fn();
|
|
917
|
+
} finally {
|
|
918
|
+
this._stack.pop();
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Create a child span that inherits the current contextId.
|
|
923
|
+
*
|
|
924
|
+
* @param spanId ID for the new span
|
|
925
|
+
* @returns A new ChronicleSpan with the current contextId
|
|
926
|
+
*/
|
|
927
|
+
static childSpan(spanId) {
|
|
928
|
+
return {
|
|
929
|
+
spanId,
|
|
930
|
+
contextId: this.current?.contextId
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
|
|
887
935
|
// src/core/pluresdb/store.ts
|
|
888
936
|
var PRAXIS_PATHS = {
|
|
889
937
|
/** Base path for all Praxis data */
|
|
@@ -919,12 +967,32 @@ var PraxisDBStore = class {
|
|
|
919
967
|
subscriptions = [];
|
|
920
968
|
factWatchers = /* @__PURE__ */ new Map();
|
|
921
969
|
onRuleError;
|
|
970
|
+
chronicle;
|
|
922
971
|
constructor(options) {
|
|
923
972
|
this.db = options.db;
|
|
924
973
|
this.registry = options.registry;
|
|
925
974
|
this.context = options.initialContext ?? {};
|
|
926
975
|
this.onRuleError = options.onRuleError ?? defaultErrorHandler;
|
|
927
976
|
}
|
|
977
|
+
/**
|
|
978
|
+
* Attach a Chronicle observer to this store.
|
|
979
|
+
*
|
|
980
|
+
* Every subsequent `storeFact` and `appendEvent` call will be recorded as a
|
|
981
|
+
* causal graph node in PluresDB, enabling full observability for free.
|
|
982
|
+
*
|
|
983
|
+
* @param chronicle Chronicle implementation to attach
|
|
984
|
+
* @returns `this` for fluent chaining
|
|
985
|
+
*
|
|
986
|
+
* @example
|
|
987
|
+
* ```typescript
|
|
988
|
+
* const store = createPraxisDBStore(db, registry)
|
|
989
|
+
* .withChronicle(createChronicle(db));
|
|
990
|
+
* ```
|
|
991
|
+
*/
|
|
992
|
+
withChronicle(chronicle) {
|
|
993
|
+
this.chronicle = chronicle;
|
|
994
|
+
return this;
|
|
995
|
+
}
|
|
928
996
|
/**
|
|
929
997
|
* Store a fact in PluresDB
|
|
930
998
|
*
|
|
@@ -939,7 +1007,31 @@ var PraxisDBStore = class {
|
|
|
939
1007
|
if (!constraintResult.valid) {
|
|
940
1008
|
throw new Error(`Constraint violation: ${constraintResult.errors.join(", ")}`);
|
|
941
1009
|
}
|
|
1010
|
+
let before;
|
|
1011
|
+
if (this.chronicle) {
|
|
1012
|
+
const payload = fact.payload;
|
|
1013
|
+
const id = payload?.id;
|
|
1014
|
+
if (id) {
|
|
1015
|
+
before = await this.getFact(fact.tag, id);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
942
1018
|
await this.persistFact(fact);
|
|
1019
|
+
if (this.chronicle) {
|
|
1020
|
+
const payload = fact.payload;
|
|
1021
|
+
const id = payload?.id ?? "";
|
|
1022
|
+
const span = ChronicleContext.current;
|
|
1023
|
+
try {
|
|
1024
|
+
await this.chronicle.record({
|
|
1025
|
+
path: getFactPath(fact.tag, id),
|
|
1026
|
+
before,
|
|
1027
|
+
after: fact,
|
|
1028
|
+
cause: span?.spanId,
|
|
1029
|
+
context: span?.contextId,
|
|
1030
|
+
metadata: { factTag: fact.tag, operation: "storeFact" }
|
|
1031
|
+
});
|
|
1032
|
+
} catch {
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
943
1035
|
await this.triggerRules([fact]);
|
|
944
1036
|
}
|
|
945
1037
|
/**
|
|
@@ -953,7 +1045,31 @@ var PraxisDBStore = class {
|
|
|
953
1045
|
throw new Error(`Constraint violation: ${constraintResult.errors.join(", ")}`);
|
|
954
1046
|
}
|
|
955
1047
|
for (const fact of facts) {
|
|
1048
|
+
let before;
|
|
1049
|
+
if (this.chronicle) {
|
|
1050
|
+
const payload = fact.payload;
|
|
1051
|
+
const id = payload?.id;
|
|
1052
|
+
if (id) {
|
|
1053
|
+
before = await this.getFact(fact.tag, id);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
956
1056
|
await this.persistFact(fact);
|
|
1057
|
+
if (this.chronicle) {
|
|
1058
|
+
const payload = fact.payload;
|
|
1059
|
+
const id = payload?.id ?? "";
|
|
1060
|
+
const span = ChronicleContext.current;
|
|
1061
|
+
try {
|
|
1062
|
+
await this.chronicle.record({
|
|
1063
|
+
path: getFactPath(fact.tag, id),
|
|
1064
|
+
before,
|
|
1065
|
+
after: fact,
|
|
1066
|
+
cause: span?.spanId,
|
|
1067
|
+
context: span?.contextId,
|
|
1068
|
+
metadata: { factTag: fact.tag, operation: "storeFacts" }
|
|
1069
|
+
});
|
|
1070
|
+
} catch {
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
957
1073
|
}
|
|
958
1074
|
await this.triggerRules(facts);
|
|
959
1075
|
}
|
|
@@ -995,7 +1111,29 @@ var PraxisDBStore = class {
|
|
|
995
1111
|
};
|
|
996
1112
|
const newEvents = [...existingEvents, entry];
|
|
997
1113
|
await this.db.set(path, newEvents);
|
|
998
|
-
|
|
1114
|
+
let eventNodeId;
|
|
1115
|
+
if (this.chronicle) {
|
|
1116
|
+
const span = ChronicleContext.current;
|
|
1117
|
+
try {
|
|
1118
|
+
const node = await this.chronicle.record({
|
|
1119
|
+
path,
|
|
1120
|
+
before: existingEvents.length > 0 ? existingEvents[existingEvents.length - 1] : void 0,
|
|
1121
|
+
after: entry,
|
|
1122
|
+
cause: span?.spanId,
|
|
1123
|
+
context: span?.contextId,
|
|
1124
|
+
metadata: { eventTag: event.tag, sequence: String(entry.sequence), operation: "appendEvent" }
|
|
1125
|
+
});
|
|
1126
|
+
eventNodeId = node.id;
|
|
1127
|
+
} catch {
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
const outerSpan = ChronicleContext.current;
|
|
1131
|
+
const ruleSpan = eventNodeId ? { spanId: eventNodeId, contextId: outerSpan?.contextId } : outerSpan;
|
|
1132
|
+
if (ruleSpan && this.chronicle) {
|
|
1133
|
+
await ChronicleContext.runAsync(ruleSpan, () => this.triggerRulesForEvents([event]));
|
|
1134
|
+
} else {
|
|
1135
|
+
await this.triggerRulesForEvents([event]);
|
|
1136
|
+
}
|
|
999
1137
|
}
|
|
1000
1138
|
/**
|
|
1001
1139
|
* Append multiple events to their respective streams
|
|
@@ -1008,6 +1146,7 @@ var PraxisDBStore = class {
|
|
|
1008
1146
|
const existing = eventsByTag.get(event.tag) ?? [];
|
|
1009
1147
|
eventsByTag.set(event.tag, [...existing, event]);
|
|
1010
1148
|
}
|
|
1149
|
+
let lastEventNodeId;
|
|
1011
1150
|
for (const [tag, tagEvents] of eventsByTag) {
|
|
1012
1151
|
const path = getEventPath(tag);
|
|
1013
1152
|
const existingEvents = await this.db.get(path) ?? [];
|
|
@@ -1018,8 +1157,30 @@ var PraxisDBStore = class {
|
|
|
1018
1157
|
sequence: sequence++
|
|
1019
1158
|
}));
|
|
1020
1159
|
await this.db.set(path, [...existingEvents, ...newEntries]);
|
|
1160
|
+
if (this.chronicle) {
|
|
1161
|
+
const span = ChronicleContext.current;
|
|
1162
|
+
for (const entry of newEntries) {
|
|
1163
|
+
try {
|
|
1164
|
+
const node = await this.chronicle.record({
|
|
1165
|
+
path,
|
|
1166
|
+
after: entry,
|
|
1167
|
+
cause: span?.spanId,
|
|
1168
|
+
context: span?.contextId,
|
|
1169
|
+
metadata: { eventTag: tag, sequence: String(entry.sequence), operation: "appendEvents" }
|
|
1170
|
+
});
|
|
1171
|
+
lastEventNodeId = node.id;
|
|
1172
|
+
} catch {
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
const outerSpan = ChronicleContext.current;
|
|
1178
|
+
const ruleSpan = lastEventNodeId ? { spanId: lastEventNodeId, contextId: outerSpan?.contextId } : outerSpan;
|
|
1179
|
+
if (ruleSpan && this.chronicle) {
|
|
1180
|
+
await ChronicleContext.runAsync(ruleSpan, () => this.triggerRulesForEvents(events));
|
|
1181
|
+
} else {
|
|
1182
|
+
await this.triggerRulesForEvents(events);
|
|
1021
1183
|
}
|
|
1022
|
-
await this.triggerRulesForEvents(events);
|
|
1023
1184
|
}
|
|
1024
1185
|
/**
|
|
1025
1186
|
* Get events from a stream
|
|
@@ -1132,6 +1293,21 @@ var PraxisDBStore = class {
|
|
|
1132
1293
|
if (constraintResult.valid) {
|
|
1133
1294
|
for (const fact of derivedFacts) {
|
|
1134
1295
|
await this.persistFact(fact);
|
|
1296
|
+
if (this.chronicle) {
|
|
1297
|
+
const payload = fact.payload;
|
|
1298
|
+
const id = payload?.id ?? "";
|
|
1299
|
+
const span = ChronicleContext.current;
|
|
1300
|
+
try {
|
|
1301
|
+
await this.chronicle.record({
|
|
1302
|
+
path: getFactPath(fact.tag, id),
|
|
1303
|
+
after: fact,
|
|
1304
|
+
cause: span?.spanId,
|
|
1305
|
+
context: span?.contextId,
|
|
1306
|
+
metadata: { factTag: fact.tag, operation: "derivedFact" }
|
|
1307
|
+
});
|
|
1308
|
+
} catch {
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1135
1311
|
}
|
|
1136
1312
|
}
|
|
1137
1313
|
}
|
|
@@ -2713,7 +2889,7 @@ function generateTauriConfig(config) {
|
|
|
2713
2889
|
|
|
2714
2890
|
// src/integrations/unified.ts
|
|
2715
2891
|
async function createUnifiedApp(config) {
|
|
2716
|
-
const { createPraxisEngine: createPraxisEngine2 } = await import("./engine-
|
|
2892
|
+
const { createPraxisEngine: createPraxisEngine2 } = await import("./engine-65QDGCAN.js");
|
|
2717
2893
|
const { createInMemoryDB: createInMemoryDB2 } = await import("./adapter-CIMBGDC7.js");
|
|
2718
2894
|
const db = config.db || createInMemoryDB2();
|
|
2719
2895
|
const pluresdb = createPluresDBAdapter({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { P as PraxisState, a as PraxisEvent, L as LogicEngine } from '../reactive-engine.svelte-Cqd8Mod2.js';
|
|
2
|
+
export { o as ReactiveEngineOptions, p as ReactiveLogicEngine, s as createReactiveEngine } from '../reactive-engine.svelte-Cqd8Mod2.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Svelte v5 Integration
|