@plures/praxis 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/FRAMEWORK.md +420 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1310 -0
  4. package/dist/adapters/cli.d.ts +43 -0
  5. package/dist/adapters/cli.d.ts.map +1 -0
  6. package/dist/adapters/cli.js +126 -0
  7. package/dist/adapters/cli.js.map +1 -0
  8. package/dist/cli/commands/auth.d.ts +26 -0
  9. package/dist/cli/commands/auth.d.ts.map +1 -0
  10. package/dist/cli/commands/auth.js +233 -0
  11. package/dist/cli/commands/auth.js.map +1 -0
  12. package/dist/cli/commands/cloud.d.ts +27 -0
  13. package/dist/cli/commands/cloud.d.ts.map +1 -0
  14. package/dist/cli/commands/cloud.js +232 -0
  15. package/dist/cli/commands/cloud.js.map +1 -0
  16. package/dist/cli/commands/generate.d.ts +25 -0
  17. package/dist/cli/commands/generate.d.ts.map +1 -0
  18. package/dist/cli/commands/generate.js +168 -0
  19. package/dist/cli/commands/generate.js.map +1 -0
  20. package/dist/cli/index.d.ts +8 -0
  21. package/dist/cli/index.d.ts.map +1 -0
  22. package/dist/cli/index.js +179 -0
  23. package/dist/cli/index.js.map +1 -0
  24. package/dist/cloud/auth.d.ts +51 -0
  25. package/dist/cloud/auth.d.ts.map +1 -0
  26. package/dist/cloud/auth.js +194 -0
  27. package/dist/cloud/auth.js.map +1 -0
  28. package/dist/cloud/billing.d.ts +184 -0
  29. package/dist/cloud/billing.d.ts.map +1 -0
  30. package/dist/cloud/billing.js +179 -0
  31. package/dist/cloud/billing.js.map +1 -0
  32. package/dist/cloud/client.d.ts +39 -0
  33. package/dist/cloud/client.d.ts.map +1 -0
  34. package/dist/cloud/client.js +176 -0
  35. package/dist/cloud/client.js.map +1 -0
  36. package/dist/cloud/index.d.ts +44 -0
  37. package/dist/cloud/index.d.ts.map +1 -0
  38. package/dist/cloud/index.js +44 -0
  39. package/dist/cloud/index.js.map +1 -0
  40. package/dist/cloud/marketplace.d.ts +166 -0
  41. package/dist/cloud/marketplace.d.ts.map +1 -0
  42. package/dist/cloud/marketplace.js +159 -0
  43. package/dist/cloud/marketplace.js.map +1 -0
  44. package/dist/cloud/provisioning.d.ts +110 -0
  45. package/dist/cloud/provisioning.d.ts.map +1 -0
  46. package/dist/cloud/provisioning.js +148 -0
  47. package/dist/cloud/provisioning.js.map +1 -0
  48. package/dist/cloud/relay/endpoints.d.ts +62 -0
  49. package/dist/cloud/relay/endpoints.d.ts.map +1 -0
  50. package/dist/cloud/relay/endpoints.js +217 -0
  51. package/dist/cloud/relay/endpoints.js.map +1 -0
  52. package/dist/cloud/relay/health/index.d.ts +5 -0
  53. package/dist/cloud/relay/health/index.d.ts.map +1 -0
  54. package/dist/cloud/relay/health/index.js +9 -0
  55. package/dist/cloud/relay/health/index.js.map +1 -0
  56. package/dist/cloud/relay/stats/index.d.ts +5 -0
  57. package/dist/cloud/relay/stats/index.d.ts.map +1 -0
  58. package/dist/cloud/relay/stats/index.js +9 -0
  59. package/dist/cloud/relay/stats/index.js.map +1 -0
  60. package/dist/cloud/relay/sync/index.d.ts +5 -0
  61. package/dist/cloud/relay/sync/index.d.ts.map +1 -0
  62. package/dist/cloud/relay/sync/index.js +9 -0
  63. package/dist/cloud/relay/sync/index.js.map +1 -0
  64. package/dist/cloud/relay/usage/index.d.ts +5 -0
  65. package/dist/cloud/relay/usage/index.d.ts.map +1 -0
  66. package/dist/cloud/relay/usage/index.js +9 -0
  67. package/dist/cloud/relay/usage/index.js.map +1 -0
  68. package/dist/cloud/sponsors.d.ts +81 -0
  69. package/dist/cloud/sponsors.d.ts.map +1 -0
  70. package/dist/cloud/sponsors.js +130 -0
  71. package/dist/cloud/sponsors.js.map +1 -0
  72. package/dist/cloud/types.d.ts +169 -0
  73. package/dist/cloud/types.d.ts.map +1 -0
  74. package/dist/cloud/types.js +7 -0
  75. package/dist/cloud/types.js.map +1 -0
  76. package/dist/components/index.d.ts +43 -0
  77. package/dist/components/index.d.ts.map +1 -0
  78. package/dist/components/index.js +17 -0
  79. package/dist/components/index.js.map +1 -0
  80. package/dist/core/actors.d.ts +95 -0
  81. package/dist/core/actors.d.ts.map +1 -0
  82. package/dist/core/actors.js +158 -0
  83. package/dist/core/actors.js.map +1 -0
  84. package/dist/core/component/generator.d.ts +122 -0
  85. package/dist/core/component/generator.d.ts.map +1 -0
  86. package/dist/core/component/generator.js +307 -0
  87. package/dist/core/component/generator.js.map +1 -0
  88. package/dist/core/engine.d.ts +92 -0
  89. package/dist/core/engine.d.ts.map +1 -0
  90. package/dist/core/engine.js +199 -0
  91. package/dist/core/engine.js.map +1 -0
  92. package/dist/core/introspection.d.ts +141 -0
  93. package/dist/core/introspection.d.ts.map +1 -0
  94. package/dist/core/introspection.js +208 -0
  95. package/dist/core/introspection.js.map +1 -0
  96. package/dist/core/logic/generator.d.ts +76 -0
  97. package/dist/core/logic/generator.d.ts.map +1 -0
  98. package/dist/core/logic/generator.js +339 -0
  99. package/dist/core/logic/generator.js.map +1 -0
  100. package/dist/core/pluresdb/generator.d.ts +58 -0
  101. package/dist/core/pluresdb/generator.d.ts.map +1 -0
  102. package/dist/core/pluresdb/generator.js +162 -0
  103. package/dist/core/pluresdb/generator.js.map +1 -0
  104. package/dist/core/protocol.d.ts +121 -0
  105. package/dist/core/protocol.d.ts.map +1 -0
  106. package/dist/core/protocol.js +46 -0
  107. package/dist/core/protocol.js.map +1 -0
  108. package/dist/core/rules.d.ts +120 -0
  109. package/dist/core/rules.d.ts.map +1 -0
  110. package/dist/core/rules.js +81 -0
  111. package/dist/core/rules.js.map +1 -0
  112. package/dist/core/schema/loader.d.ts +47 -0
  113. package/dist/core/schema/loader.d.ts.map +1 -0
  114. package/dist/core/schema/loader.js +189 -0
  115. package/dist/core/schema/loader.js.map +1 -0
  116. package/dist/core/schema/normalize.d.ts +72 -0
  117. package/dist/core/schema/normalize.d.ts.map +1 -0
  118. package/dist/core/schema/normalize.js +190 -0
  119. package/dist/core/schema/normalize.js.map +1 -0
  120. package/dist/core/schema/types.d.ts +370 -0
  121. package/dist/core/schema/types.d.ts.map +1 -0
  122. package/dist/core/schema/types.js +161 -0
  123. package/dist/core/schema/types.js.map +1 -0
  124. package/dist/dsl/index.d.ts +152 -0
  125. package/dist/dsl/index.d.ts.map +1 -0
  126. package/dist/dsl/index.js +132 -0
  127. package/dist/dsl/index.js.map +1 -0
  128. package/dist/dsl.d.ts +124 -0
  129. package/dist/dsl.d.ts.map +1 -0
  130. package/dist/dsl.js +130 -0
  131. package/dist/dsl.js.map +1 -0
  132. package/dist/examples/advanced-todo/index.d.ts +55 -0
  133. package/dist/examples/advanced-todo/index.d.ts.map +1 -0
  134. package/dist/examples/advanced-todo/index.js +222 -0
  135. package/dist/examples/advanced-todo/index.js.map +1 -0
  136. package/dist/examples/auth-basic/index.d.ts +17 -0
  137. package/dist/examples/auth-basic/index.d.ts.map +1 -0
  138. package/dist/examples/auth-basic/index.js +122 -0
  139. package/dist/examples/auth-basic/index.js.map +1 -0
  140. package/dist/examples/cart/index.d.ts +19 -0
  141. package/dist/examples/cart/index.d.ts.map +1 -0
  142. package/dist/examples/cart/index.js +202 -0
  143. package/dist/examples/cart/index.js.map +1 -0
  144. package/dist/examples/hero-ecommerce/index.d.ts +39 -0
  145. package/dist/examples/hero-ecommerce/index.d.ts.map +1 -0
  146. package/dist/examples/hero-ecommerce/index.js +506 -0
  147. package/dist/examples/hero-ecommerce/index.js.map +1 -0
  148. package/dist/examples/svelte-counter/index.d.ts +31 -0
  149. package/dist/examples/svelte-counter/index.d.ts.map +1 -0
  150. package/dist/examples/svelte-counter/index.js +123 -0
  151. package/dist/examples/svelte-counter/index.js.map +1 -0
  152. package/dist/flows.d.ts +125 -0
  153. package/dist/flows.d.ts.map +1 -0
  154. package/dist/flows.js +160 -0
  155. package/dist/flows.js.map +1 -0
  156. package/dist/index.d.ts +67 -0
  157. package/dist/index.d.ts.map +1 -0
  158. package/dist/index.js +59 -0
  159. package/dist/index.js.map +1 -0
  160. package/dist/integrations/pluresdb.d.ts +56 -0
  161. package/dist/integrations/pluresdb.d.ts.map +1 -0
  162. package/dist/integrations/pluresdb.js +46 -0
  163. package/dist/integrations/pluresdb.js.map +1 -0
  164. package/dist/integrations/svelte.d.ts +306 -0
  165. package/dist/integrations/svelte.d.ts.map +1 -0
  166. package/dist/integrations/svelte.js +447 -0
  167. package/dist/integrations/svelte.js.map +1 -0
  168. package/dist/registry.d.ts +94 -0
  169. package/dist/registry.d.ts.map +1 -0
  170. package/dist/registry.js +181 -0
  171. package/dist/registry.js.map +1 -0
  172. package/dist/runtime/terminal-adapter.d.ts +105 -0
  173. package/dist/runtime/terminal-adapter.d.ts.map +1 -0
  174. package/dist/runtime/terminal-adapter.js +113 -0
  175. package/dist/runtime/terminal-adapter.js.map +1 -0
  176. package/dist/step.d.ts +34 -0
  177. package/dist/step.d.ts.map +1 -0
  178. package/dist/step.js +111 -0
  179. package/dist/step.js.map +1 -0
  180. package/dist/types.d.ts +63 -0
  181. package/dist/types.d.ts.map +1 -0
  182. package/dist/types.js +6 -0
  183. package/dist/types.js.map +1 -0
  184. package/docs/MONETIZATION.md +394 -0
  185. package/docs/TERMINAL_NODE.md +588 -0
  186. package/docs/guides/canvas.md +389 -0
  187. package/docs/guides/getting-started.md +347 -0
  188. package/docs/guides/history-state-pattern.md +618 -0
  189. package/docs/guides/orchestration.md +617 -0
  190. package/docs/guides/parallel-state-pattern.md +767 -0
  191. package/docs/guides/svelte-integration.md +691 -0
  192. package/package.json +96 -0
  193. package/src/__tests__/actors.test.ts +270 -0
  194. package/src/__tests__/billing.test.ts +175 -0
  195. package/src/__tests__/cloud.test.ts +247 -0
  196. package/src/__tests__/dsl.test.ts +154 -0
  197. package/src/__tests__/edge-cases.test.ts +475 -0
  198. package/src/__tests__/engine.test.ts +137 -0
  199. package/src/__tests__/generators.test.ts +270 -0
  200. package/src/__tests__/introspection.test.ts +321 -0
  201. package/src/__tests__/protocol.test.ts +40 -0
  202. package/src/__tests__/provisioning.test.ts +162 -0
  203. package/src/__tests__/schema.test.ts +241 -0
  204. package/src/__tests__/svelte-integration.test.ts +431 -0
  205. package/src/__tests__/terminal-node.test.ts +352 -0
  206. package/src/adapters/cli.ts +175 -0
  207. package/src/cli/commands/auth.ts +271 -0
  208. package/src/cli/commands/cloud.ts +281 -0
  209. package/src/cli/commands/generate.ts +225 -0
  210. package/src/cli/index.ts +190 -0
  211. package/src/cloud/README.md +383 -0
  212. package/src/cloud/auth.ts +245 -0
  213. package/src/cloud/billing.ts +336 -0
  214. package/src/cloud/client.ts +221 -0
  215. package/src/cloud/index.ts +121 -0
  216. package/src/cloud/marketplace.ts +303 -0
  217. package/src/cloud/provisioning.ts +254 -0
  218. package/src/cloud/relay/endpoints.ts +307 -0
  219. package/src/cloud/relay/health/function.json +17 -0
  220. package/src/cloud/relay/health/index.ts +10 -0
  221. package/src/cloud/relay/host.json +15 -0
  222. package/src/cloud/relay/local.settings.json +8 -0
  223. package/src/cloud/relay/stats/function.json +17 -0
  224. package/src/cloud/relay/stats/index.ts +10 -0
  225. package/src/cloud/relay/sync/function.json +17 -0
  226. package/src/cloud/relay/sync/index.ts +10 -0
  227. package/src/cloud/relay/usage/function.json +17 -0
  228. package/src/cloud/relay/usage/index.ts +10 -0
  229. package/src/cloud/sponsors.ts +213 -0
  230. package/src/cloud/types.ts +198 -0
  231. package/src/components/README.md +125 -0
  232. package/src/components/TerminalNode.svelte +457 -0
  233. package/src/components/index.ts +46 -0
  234. package/src/core/actors.ts +205 -0
  235. package/src/core/component/generator.ts +432 -0
  236. package/src/core/engine.ts +243 -0
  237. package/src/core/introspection.ts +329 -0
  238. package/src/core/logic/generator.ts +420 -0
  239. package/src/core/pluresdb/generator.ts +229 -0
  240. package/src/core/protocol.ts +132 -0
  241. package/src/core/rules.ts +167 -0
  242. package/src/core/schema/loader.ts +247 -0
  243. package/src/core/schema/normalize.ts +322 -0
  244. package/src/core/schema/types.ts +557 -0
  245. package/src/dsl/index.ts +218 -0
  246. package/src/dsl.ts +214 -0
  247. package/src/examples/advanced-todo/App.svelte +506 -0
  248. package/src/examples/advanced-todo/README.md +371 -0
  249. package/src/examples/advanced-todo/index.ts +309 -0
  250. package/src/examples/auth-basic/index.ts +163 -0
  251. package/src/examples/cart/index.ts +259 -0
  252. package/src/examples/hero-ecommerce/index.ts +657 -0
  253. package/src/examples/svelte-counter/index.ts +168 -0
  254. package/src/flows.ts +268 -0
  255. package/src/index.ts +154 -0
  256. package/src/integrations/pluresdb.ts +93 -0
  257. package/src/integrations/svelte.ts +617 -0
  258. package/src/registry.ts +223 -0
  259. package/src/runtime/terminal-adapter.ts +175 -0
  260. package/src/step.ts +151 -0
  261. package/src/types.ts +70 -0
  262. package/templates/basic-app/README.md +147 -0
  263. package/templates/fullstack-app/README.md +279 -0
@@ -0,0 +1,767 @@
1
+ # Parallel State Pattern in Praxis
2
+
3
+ This guide explains how to implement parallel states in Praxis using multiple engines, similar to XState's parallel states.
4
+
5
+ ## Overview
6
+
7
+ Parallel states allow different parts of your application to maintain independent state machines that run concurrently. This is useful for:
8
+
9
+ - **Multi-region UIs**: Different panels operating independently
10
+ - **Concurrent Workflows**: Multiple processes running simultaneously
11
+ - **Complex Orchestration**: Coordinating independent subsystems
12
+ - **Feature Isolation**: Separate state machines for different features
13
+
14
+ ## Table of Contents
15
+
16
+ 1. [Multiple Engine Pattern](#multiple-engine-pattern)
17
+ 2. [Coordinated Engines](#coordinated-engines)
18
+ 3. [Parent-Child Hierarchy](#parent-child-hierarchy)
19
+ 4. [Cross-Engine Communication](#cross-engine-communication)
20
+ 5. [Svelte Integration](#svelte-integration)
21
+ 6. [Real-World Examples](#real-world-examples)
22
+
23
+ ## Multiple Engine Pattern
24
+
25
+ The simplest way to achieve parallel states is to create multiple independent engines.
26
+
27
+ ### Basic Example
28
+
29
+ ```typescript
30
+ import { createPraxisEngine, PraxisRegistry } from '@plures/praxis';
31
+
32
+ // Engine 1: Authentication
33
+ interface AuthContext {
34
+ user: User | null;
35
+ isLoading: boolean;
36
+ }
37
+
38
+ const authRegistry = new PraxisRegistry<AuthContext>();
39
+ // ... register auth rules
40
+ const authEngine = createPraxisEngine({
41
+ initialContext: { user: null, isLoading: false },
42
+ registry: authRegistry,
43
+ });
44
+
45
+ // Engine 2: Shopping Cart
46
+ interface CartContext {
47
+ items: CartItem[];
48
+ total: number;
49
+ }
50
+
51
+ const cartRegistry = new PraxisRegistry<CartContext>();
52
+ // ... register cart rules
53
+ const cartEngine = createPraxisEngine({
54
+ initialContext: { items: [], total: 0 },
55
+ registry: cartRegistry,
56
+ });
57
+
58
+ // Engine 3: Notifications
59
+ interface NotificationContext {
60
+ notifications: Notification[];
61
+ unreadCount: number;
62
+ }
63
+
64
+ const notificationRegistry = new PraxisRegistry<NotificationContext>();
65
+ // ... register notification rules
66
+ const notificationEngine = createPraxisEngine({
67
+ initialContext: { notifications: [], unreadCount: 0 },
68
+ registry: notificationRegistry,
69
+ });
70
+
71
+ // Use all engines in parallel
72
+ authEngine.step([Login.create({ username: 'alice' })]);
73
+ cartEngine.step([AddItem.create({ itemId: '123' })]);
74
+ notificationEngine.step([ShowNotification.create({ message: 'Welcome!' })]);
75
+ ```
76
+
77
+ ### Independent Operation
78
+
79
+ Each engine operates completely independently:
80
+
81
+ ```typescript
82
+ // Each engine maintains its own state
83
+ console.log('Auth:', authEngine.getContext());
84
+ console.log('Cart:', cartEngine.getContext());
85
+ console.log('Notifications:', notificationEngine.getContext());
86
+
87
+ // Events are processed independently
88
+ authEngine.step([Login.create({})]); // Only affects auth
89
+ cartEngine.step([Checkout.create({})]); // Only affects cart
90
+ notificationEngine.step([Clear.create({})]); // Only affects notifications
91
+ ```
92
+
93
+ ## Coordinated Engines
94
+
95
+ When engines need to coordinate, you can create a coordinator layer:
96
+
97
+ ### Coordinator Pattern
98
+
99
+ ```typescript
100
+ interface CoordinatorEvents {
101
+ auth: PraxisEvent[];
102
+ cart: PraxisEvent[];
103
+ notifications: PraxisEvent[];
104
+ }
105
+
106
+ class AppCoordinator {
107
+ constructor(
108
+ private authEngine: LogicEngine<AuthContext>,
109
+ private cartEngine: LogicEngine<CartContext>,
110
+ private notificationEngine: LogicEngine<NotificationContext>
111
+ ) {}
112
+
113
+ /**
114
+ * Dispatch events to appropriate engines
115
+ */
116
+ dispatch(events: CoordinatorEvents) {
117
+ const results = {
118
+ auth: this.authEngine.step(events.auth),
119
+ cart: this.cartEngine.step(events.cart),
120
+ notifications: this.notificationEngine.step(events.notifications),
121
+ };
122
+
123
+ // Cross-engine reactions
124
+ this.handleCrossEngineEffects(results);
125
+
126
+ return results;
127
+ }
128
+
129
+ /**
130
+ * Handle cross-engine effects
131
+ */
132
+ private handleCrossEngineEffects(results: any) {
133
+ // When user logs out, clear cart
134
+ if (results.auth.state.facts.some((f: any) => f.tag === 'UserLoggedOut')) {
135
+ this.cartEngine.step([ClearCart.create({})]);
136
+ this.notificationEngine.step([
137
+ ShowNotification.create({ message: 'Cart cleared' })
138
+ ]);
139
+ }
140
+
141
+ // When order is placed, show notification
142
+ if (results.cart.state.facts.some((f: any) => f.tag === 'OrderPlaced')) {
143
+ this.notificationEngine.step([
144
+ ShowNotification.create({ message: 'Order placed successfully!' })
145
+ ]);
146
+ }
147
+
148
+ // When cart is updated, check auth
149
+ if (results.cart.state.facts.some((f: any) => f.tag === 'CartUpdated')) {
150
+ const authContext = this.authEngine.getContext();
151
+ if (!authContext.user) {
152
+ this.notificationEngine.step([
153
+ ShowNotification.create({ message: 'Please login to continue' })
154
+ ]);
155
+ }
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Get all contexts
161
+ */
162
+ getState() {
163
+ return {
164
+ auth: this.authEngine.getContext(),
165
+ cart: this.cartEngine.getContext(),
166
+ notifications: this.notificationEngine.getContext(),
167
+ };
168
+ }
169
+ }
170
+
171
+ // Usage
172
+ const coordinator = new AppCoordinator(
173
+ authEngine,
174
+ cartEngine,
175
+ notificationEngine
176
+ );
177
+
178
+ coordinator.dispatch({
179
+ auth: [Login.create({ username: 'alice' })],
180
+ cart: [AddItem.create({ itemId: '123' })],
181
+ notifications: [],
182
+ });
183
+
184
+ const state = coordinator.getState();
185
+ console.log('Current state:', state);
186
+ ```
187
+
188
+ ### Event Bus Pattern
189
+
190
+ For more complex coordination, use an event bus:
191
+
192
+ ```typescript
193
+ type EventHandler = (event: PraxisEvent) => void;
194
+
195
+ class EventBus {
196
+ private handlers = new Map<string, Set<EventHandler>>();
197
+
198
+ subscribe(eventType: string, handler: EventHandler) {
199
+ if (!this.handlers.has(eventType)) {
200
+ this.handlers.set(eventType, new Set());
201
+ }
202
+ this.handlers.get(eventType)!.add(handler);
203
+
204
+ // Return unsubscribe function
205
+ return () => {
206
+ this.handlers.get(eventType)?.delete(handler);
207
+ };
208
+ }
209
+
210
+ publish(event: PraxisEvent) {
211
+ const handlers = this.handlers.get(event.tag);
212
+ if (handlers) {
213
+ handlers.forEach(handler => handler(event));
214
+ }
215
+ }
216
+ }
217
+
218
+ // Create shared event bus
219
+ const eventBus = new EventBus();
220
+
221
+ // Connect engines to event bus
222
+ eventBus.subscribe('USER_LOGGED_OUT', () => {
223
+ cartEngine.step([ClearCart.create({})]);
224
+ notificationEngine.step([
225
+ ShowNotification.create({ message: 'Cart cleared' })
226
+ ]);
227
+ });
228
+
229
+ eventBus.subscribe('ORDER_PLACED', (event) => {
230
+ notificationEngine.step([
231
+ ShowNotification.create({ message: 'Order placed!' })
232
+ ]);
233
+ });
234
+
235
+ eventBus.subscribe('CART_ITEM_ADDED', (event) => {
236
+ const authContext = authEngine.getContext();
237
+ if (!authContext.user) {
238
+ notificationEngine.step([
239
+ ShowNotification.create({ message: 'Login to save your cart' })
240
+ ]);
241
+ }
242
+ });
243
+
244
+ // Dispatch events through bus
245
+ function dispatchWithBroadcast(engine: LogicEngine<any>, events: PraxisEvent[]) {
246
+ const result = engine.step(events);
247
+
248
+ // Broadcast facts as events
249
+ result.state.facts.forEach(fact => {
250
+ eventBus.publish({
251
+ tag: fact.tag,
252
+ payload: fact.payload,
253
+ timestamp: Date.now(),
254
+ });
255
+ });
256
+
257
+ return result;
258
+ }
259
+ ```
260
+
261
+ ## Parent-Child Hierarchy
262
+
263
+ For hierarchical state management:
264
+
265
+ ```typescript
266
+ interface ParentContext {
267
+ mode: 'idle' | 'working' | 'error';
268
+ children: Map<string, any>;
269
+ }
270
+
271
+ class HierarchicalEngine {
272
+ private parentEngine: LogicEngine<ParentContext>;
273
+ private childEngines = new Map<string, LogicEngine<any>>();
274
+
275
+ constructor(parentEngine: LogicEngine<ParentContext>) {
276
+ this.parentEngine = parentEngine;
277
+ }
278
+
279
+ addChild<T>(id: string, engine: LogicEngine<T>) {
280
+ this.childEngines.set(id, engine);
281
+
282
+ // Update parent context
283
+ const parentContext = this.parentEngine.getContext();
284
+ parentContext.children.set(id, engine.getContext());
285
+ }
286
+
287
+ removeChild(id: string) {
288
+ this.childEngines.delete(id);
289
+
290
+ const parentContext = this.parentEngine.getContext();
291
+ parentContext.children.delete(id);
292
+ }
293
+
294
+ dispatch(parentEvents: PraxisEvent[], childEvents: Map<string, PraxisEvent[]>) {
295
+ // Process parent events
296
+ this.parentEngine.step(parentEvents);
297
+
298
+ // Process child events
299
+ for (const [childId, events] of childEvents) {
300
+ const child = this.childEngines.get(childId);
301
+ if (child) {
302
+ child.step(events);
303
+ }
304
+ }
305
+
306
+ // Update parent's view of children
307
+ const parentContext = this.parentEngine.getContext();
308
+ for (const [id, child] of this.childEngines) {
309
+ parentContext.children.set(id, child.getContext());
310
+ }
311
+ }
312
+
313
+ getState() {
314
+ return {
315
+ parent: this.parentEngine.getContext(),
316
+ children: Array.from(this.childEngines.entries()).map(([id, engine]) => ({
317
+ id,
318
+ context: engine.getContext(),
319
+ })),
320
+ };
321
+ }
322
+ }
323
+
324
+ // Usage
325
+ const parentEngine = createPraxisEngine<ParentContext>({
326
+ initialContext: {
327
+ mode: 'idle',
328
+ children: new Map(),
329
+ },
330
+ registry: parentRegistry,
331
+ });
332
+
333
+ const hierarchical = new HierarchicalEngine(parentEngine);
334
+
335
+ // Add child engines
336
+ hierarchical.addChild('auth', authEngine);
337
+ hierarchical.addChild('cart', cartEngine);
338
+ hierarchical.addChild('notifications', notificationEngine);
339
+
340
+ // Dispatch to multiple levels
341
+ hierarchical.dispatch(
342
+ [ParentModeChange.create({ mode: 'working' })],
343
+ new Map([
344
+ ['auth', [Login.create({})]],
345
+ ['cart', [AddItem.create({ itemId: '123' })]],
346
+ ])
347
+ );
348
+ ```
349
+
350
+ ## Cross-Engine Communication
351
+
352
+ ### Shared Context Pattern
353
+
354
+ ```typescript
355
+ interface SharedState {
356
+ userId: string | null;
357
+ cartTotal: number;
358
+ unreadCount: number;
359
+ }
360
+
361
+ class SharedStateManager {
362
+ private state: SharedState = {
363
+ userId: null,
364
+ cartTotal: 0,
365
+ unreadCount: 0,
366
+ };
367
+
368
+ private subscribers = new Set<(state: SharedState) => void>();
369
+
370
+ getState(): SharedState {
371
+ return { ...this.state };
372
+ }
373
+
374
+ update(partial: Partial<SharedState>) {
375
+ this.state = { ...this.state, ...partial };
376
+ this.notify();
377
+ }
378
+
379
+ subscribe(callback: (state: SharedState) => void) {
380
+ this.subscribers.add(callback);
381
+ callback(this.state);
382
+
383
+ return () => {
384
+ this.subscribers.delete(callback);
385
+ };
386
+ }
387
+
388
+ private notify() {
389
+ this.subscribers.forEach(sub => sub(this.state));
390
+ }
391
+ }
392
+
393
+ // Create shared state
394
+ const sharedState = new SharedStateManager();
395
+
396
+ // Connect engines to shared state
397
+ sharedState.subscribe((state) => {
398
+ // Update cart engine when user changes
399
+ if (state.userId) {
400
+ cartEngine.step([LoadUserCart.create({ userId: state.userId })]);
401
+ }
402
+ });
403
+
404
+ sharedState.subscribe((state) => {
405
+ // Update notification badge
406
+ notificationEngine.step([
407
+ UpdateBadge.create({ count: state.unreadCount })
408
+ ]);
409
+ });
410
+
411
+ // Engines update shared state
412
+ authEngine.step([Login.create({})]);
413
+ sharedState.update({ userId: authEngine.getContext().user?.id || null });
414
+
415
+ cartEngine.step([UpdateCart.create({})]);
416
+ sharedState.update({ cartTotal: cartEngine.getContext().total });
417
+ ```
418
+
419
+ ## Svelte Integration
420
+
421
+ Using parallel engines with Svelte 5:
422
+
423
+ ### Multiple Engines in Component
424
+
425
+ ```svelte
426
+ <script lang="ts">
427
+ import { usePraxisEngine } from '@plures/praxis/svelte';
428
+ import {
429
+ createAuthEngine,
430
+ createCartEngine,
431
+ createNotificationEngine
432
+ } from './engines';
433
+
434
+ // Create independent engines
435
+ const authEngine = createAuthEngine();
436
+ const cartEngine = createCartEngine();
437
+ const notificationEngine = createNotificationEngine();
438
+
439
+ // Bind to Svelte
440
+ const auth = usePraxisEngine(authEngine);
441
+ const cart = usePraxisEngine(cartEngine);
442
+ const notifications = usePraxisEngine(notificationEngine);
443
+
444
+ // Cross-engine reactions
445
+ $: if (auth.context.user) {
446
+ cart.dispatch([LoadUserCart.create({
447
+ userId: auth.context.user.id
448
+ })]);
449
+ }
450
+
451
+ $: if (cart.context.items.length > 0 && !auth.context.user) {
452
+ notifications.dispatch([ShowNotification.create({
453
+ message: 'Please login to continue'
454
+ })]);
455
+ }
456
+ </script>
457
+
458
+ <div class="app">
459
+ <!-- Auth UI -->
460
+ <header>
461
+ {#if auth.context.user}
462
+ <p>Welcome, {auth.context.user.name}!</p>
463
+ <button onclick={() => auth.dispatch([Logout.create({})])}>
464
+ Logout
465
+ </button>
466
+ {:else}
467
+ <button onclick={() => auth.dispatch([Login.create({})])}>
468
+ Login
469
+ </button>
470
+ {/if}
471
+ </header>
472
+
473
+ <!-- Cart UI -->
474
+ <main>
475
+ <h2>Shopping Cart ({cart.context.items.length})</h2>
476
+ <ul>
477
+ {#each cart.context.items as item}
478
+ <li>{item.name} - ${item.price}</li>
479
+ {/each}
480
+ </ul>
481
+ <p>Total: ${cart.context.total}</p>
482
+ </main>
483
+
484
+ <!-- Notifications UI -->
485
+ <aside>
486
+ {#each notifications.context.notifications as notification}
487
+ <div class="notification">
488
+ {notification.message}
489
+ </div>
490
+ {/each}
491
+ </aside>
492
+ </div>
493
+ ```
494
+
495
+ ### Coordinator Component
496
+
497
+ ```svelte
498
+ <script lang="ts">
499
+ import { usePraxisEngine } from '@plures/praxis/svelte';
500
+ import { AppCoordinator } from './coordinator';
501
+ import {
502
+ createAuthEngine,
503
+ createCartEngine,
504
+ createNotificationEngine
505
+ } from './engines';
506
+
507
+ // Create coordinator
508
+ const coordinator = new AppCoordinator(
509
+ createAuthEngine(),
510
+ createCartEngine(),
511
+ createNotificationEngine()
512
+ );
513
+
514
+ // Track overall state
515
+ let state = $state(coordinator.getState());
516
+
517
+ function dispatch(events: CoordinatorEvents) {
518
+ coordinator.dispatch(events);
519
+ state = coordinator.getState();
520
+ }
521
+ </script>
522
+
523
+ <div class="app">
524
+ <header>
525
+ <p>User: {state.auth.user?.name || 'Guest'}</p>
526
+ <p>Cart: {state.cart.items.length} items</p>
527
+ <p>Notifications: {state.notifications.unreadCount}</p>
528
+ </header>
529
+
530
+ <main>
531
+ <button onclick={() => dispatch({
532
+ auth: [Login.create({})],
533
+ cart: [],
534
+ notifications: []
535
+ })}>
536
+ Login
537
+ </button>
538
+
539
+ <button onclick={() => dispatch({
540
+ auth: [],
541
+ cart: [AddItem.create({ itemId: '123' })],
542
+ notifications: []
543
+ })}>
544
+ Add to Cart
545
+ </button>
546
+ </main>
547
+ </div>
548
+ ```
549
+
550
+ ## Real-World Examples
551
+
552
+ ### E-Commerce Application
553
+
554
+ ```typescript
555
+ // Create engines for different concerns
556
+ const engines = {
557
+ auth: createAuthEngine(),
558
+ catalog: createCatalogEngine(),
559
+ cart: createCartEngine(),
560
+ checkout: createCheckoutEngine(),
561
+ notifications: createNotificationEngine(),
562
+ };
563
+
564
+ // Set up cross-engine coordination
565
+ function setupCoordination() {
566
+ // When user logs in, load their cart
567
+ authEngine.subscribe((state) => {
568
+ if (state.context.user) {
569
+ cartEngine.step([
570
+ LoadCart.create({ userId: state.context.user.id })
571
+ ]);
572
+ }
573
+ });
574
+
575
+ // When cart updates, update checkout
576
+ cartEngine.subscribe((state) => {
577
+ checkoutEngine.step([
578
+ UpdateTotal.create({ total: state.context.total })
579
+ ]);
580
+ });
581
+
582
+ // When checkout completes, clear cart and show notification
583
+ checkoutEngine.subscribe((state) => {
584
+ if (state.facts.some(f => f.tag === 'OrderPlaced')) {
585
+ cartEngine.step([ClearCart.create({})]);
586
+ notificationEngine.step([
587
+ ShowNotification.create({ message: 'Order placed!' })
588
+ ]);
589
+ }
590
+ });
591
+ }
592
+ ```
593
+
594
+ ### Multi-Panel Dashboard
595
+
596
+ ```typescript
597
+ // Create engine for each dashboard panel
598
+ const panels = {
599
+ metrics: createMetricsEngine(),
600
+ logs: createLogsEngine(),
601
+ alerts: createAlertsEngine(),
602
+ settings: createSettingsEngine(),
603
+ };
604
+
605
+ // Each panel operates independently
606
+ function updatePanel(panelId: keyof typeof panels, events: PraxisEvent[]) {
607
+ panels[panelId].step(events);
608
+ }
609
+
610
+ // Shared coordination for common actions
611
+ function refreshAll() {
612
+ Object.values(panels).forEach(panel => {
613
+ panel.step([Refresh.create({})]);
614
+ });
615
+ }
616
+
617
+ function exportAll() {
618
+ const data = Object.entries(panels).map(([id, panel]) => ({
619
+ panel: id,
620
+ data: panel.getContext(),
621
+ }));
622
+
623
+ downloadJSON(data, 'dashboard-export.json');
624
+ }
625
+ ```
626
+
627
+ ### Multiplayer Game
628
+
629
+ ```typescript
630
+ // Engine for each game aspect
631
+ const game = {
632
+ player: createPlayerEngine(),
633
+ inventory: createInventoryEngine(),
634
+ chat: createChatEngine(),
635
+ world: createWorldEngine(),
636
+ };
637
+
638
+ // Process game tick
639
+ function gameTick(deltaTime: number) {
640
+ // Update all engines
641
+ game.player.step([Tick.create({ deltaTime })]);
642
+ game.inventory.step([Tick.create({ deltaTime })]);
643
+ game.world.step([Tick.create({ deltaTime })]);
644
+
645
+ // Sync states
646
+ const playerPos = game.player.getContext().position;
647
+ game.world.step([UpdatePlayerPosition.create({ position: playerPos })]);
648
+ }
649
+
650
+ // Handle player actions
651
+ function handlePlayerAction(action: string) {
652
+ switch (action) {
653
+ case 'USE_ITEM':
654
+ const item = game.inventory.getContext().selectedItem;
655
+ game.player.step([UseItem.create({ item })]);
656
+ game.inventory.step([RemoveItem.create({ item })]);
657
+ break;
658
+
659
+ case 'SEND_MESSAGE':
660
+ const player = game.player.getContext();
661
+ game.chat.step([SendMessage.create({
662
+ player: player.name,
663
+ message: player.currentMessage
664
+ })]);
665
+ break;
666
+ }
667
+ }
668
+ ```
669
+
670
+ ## Best Practices
671
+
672
+ ### 1. Clear Boundaries
673
+
674
+ ```typescript
675
+ // ✅ Good: Clear separation of concerns
676
+ const userEngine = createUserEngine(); // User state only
677
+ const ordersEngine = createOrdersEngine(); // Orders only
678
+ const cartEngine = createCartEngine(); // Cart only
679
+
680
+ // ❌ Bad: Mixing concerns
681
+ const everythingEngine = createEverythingEngine(); // Too much
682
+ ```
683
+
684
+ ### 2. Minimal Communication
685
+
686
+ ```typescript
687
+ // ✅ Good: Explicit coordination points
688
+ eventBus.subscribe('USER_LOGGED_OUT', () => {
689
+ cartEngine.step([ClearCart.create({})]);
690
+ });
691
+
692
+ // ❌ Bad: Tight coupling
693
+ cartEngine.setAuthEngine(authEngine); // Don't do this
694
+ ```
695
+
696
+ ### 3. Independent Testing
697
+
698
+ ```typescript
699
+ // ✅ Each engine can be tested independently
700
+ describe('Cart Engine', () => {
701
+ it('should add items', () => {
702
+ const engine = createCartEngine();
703
+ engine.step([AddItem.create({ itemId: '123' })]);
704
+ expect(engine.getContext().items.length).toBe(1);
705
+ });
706
+ });
707
+
708
+ describe('Auth Engine', () => {
709
+ it('should login users', () => {
710
+ const engine = createAuthEngine();
711
+ engine.step([Login.create({ username: 'alice' })]);
712
+ expect(engine.getContext().user).toBeTruthy();
713
+ });
714
+ });
715
+ ```
716
+
717
+ ### 4. Document Dependencies
718
+
719
+ ```typescript
720
+ /**
721
+ * Shopping Cart Engine
722
+ *
723
+ * Dependencies:
724
+ * - Auth engine: Requires user ID to load cart
725
+ * - Checkout engine: Sends total for checkout
726
+ *
727
+ * Emits:
728
+ * - CartUpdated: When items change
729
+ * - CartCleared: When cart is emptied
730
+ */
731
+ export function createCartEngine() {
732
+ // ...
733
+ }
734
+ ```
735
+
736
+ ## Comparison with XState
737
+
738
+ | Feature | XState | Praxis |
739
+ |---------|--------|--------|
740
+ | **Parallel States** | `type: 'parallel'` | Multiple engines |
741
+ | **Coordination** | Parent state machine | Coordinator pattern |
742
+ | **Communication** | Event forwarding | Event bus or shared state |
743
+ | **Hierarchy** | Nested states | Parent-child engines |
744
+ | **Testing** | Test parent machine | Test each engine independently |
745
+
746
+ ## Summary
747
+
748
+ The parallel state pattern in Praxis provides:
749
+
750
+ - ✅ **Independent Engines**: Each subsystem has its own engine
751
+ - ✅ **Flexible Coordination**: Event bus, coordinator, or shared state
752
+ - ✅ **Clear Boundaries**: Separation of concerns
753
+ - ✅ **Easy Testing**: Test engines independently
754
+ - ✅ **Svelte Integration**: Use multiple engines in components
755
+ - ✅ **Scalable**: Add or remove engines as needed
756
+
757
+ Choose the right pattern based on your needs:
758
+ - **Independent engines**: When subsystems don't interact
759
+ - **Event bus**: When you need loose coupling with pub/sub
760
+ - **Coordinator**: When you need centralized orchestration
761
+ - **Shared state**: When engines need to share data
762
+ - **Hierarchy**: When you have parent-child relationships
763
+
764
+ For more examples, see:
765
+ - [E-Commerce Example](/src/examples/hero-ecommerce/)
766
+ - [Auth Example](/src/examples/auth-basic/)
767
+ - [Multiple Engine Tests](/src/__tests__/edge-cases.test.ts)