@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,691 @@
1
+ # Praxis Svelte 5 Integration Guide
2
+
3
+ Complete guide to using Praxis with Svelte 5, including both traditional stores and modern runes.
4
+
5
+ ## Overview
6
+
7
+ Praxis provides first-class Svelte 5 support through the `@plures/praxis/svelte` export. The integration offers:
8
+
9
+ - ✅ **Traditional Stores**: Backward-compatible store API
10
+ - ✅ **Svelte 5 Runes**: Modern `$state`, `$derived`, `$effect` support
11
+ - ✅ **History & Time-Travel**: Built-in undo/redo and debugging
12
+ - ✅ **Type Safety**: Full TypeScript support
13
+ - ✅ **Automatic Cleanup**: No memory leaks
14
+ - ✅ **Zero Config**: Works out of the box
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @plures/praxis svelte@^5.0.0
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### Option 1: Store API (Traditional)
25
+
26
+ ```svelte
27
+ <script lang="ts">
28
+ import { createPraxisStore } from '@plures/praxis/svelte';
29
+ import { myEngine, Increment } from './my-engine';
30
+
31
+ const store = createPraxisStore(myEngine);
32
+ </script>
33
+
34
+ <div>
35
+ <p>Count: {$store.context.count}</p>
36
+ <button onclick={() => store.dispatch([Increment.create({})])}>
37
+ Increment
38
+ </button>
39
+ </div>
40
+ ```
41
+
42
+ ### Option 2: Runes API (Svelte 5)
43
+
44
+ ```svelte
45
+ <script lang="ts">
46
+ import { usePraxisEngine } from '@plures/praxis/svelte';
47
+ import { myEngine, Increment } from './my-engine';
48
+
49
+ const { context, dispatch } = usePraxisEngine(myEngine);
50
+ </script>
51
+
52
+ <div>
53
+ <p>Count: {context.count}</p>
54
+ <button onclick={() => dispatch([Increment.create({})])}>
55
+ Increment
56
+ </button>
57
+ </div>
58
+ ```
59
+
60
+ ## Store API
61
+
62
+ ### createPraxisStore
63
+
64
+ Creates a Svelte store that tracks the full engine state.
65
+
66
+ ```typescript
67
+ import { createPraxisStore } from '@plures/praxis/svelte';
68
+ import type { Readable } from '@plures/praxis/svelte';
69
+
70
+ const store = createPraxisStore(engine);
71
+
72
+ // Subscribe to changes
73
+ const unsubscribe = store.subscribe((state) => {
74
+ console.log('State:', state.context);
75
+ console.log('Facts:', state.facts);
76
+ });
77
+
78
+ // Dispatch events
79
+ store.dispatch([MyEvent.create({ data: 'value' })]);
80
+
81
+ // Clean up
82
+ unsubscribe();
83
+ ```
84
+
85
+ **Use in Svelte:**
86
+
87
+ ```svelte
88
+ <script>
89
+ import { createPraxisStore } from '@plures/praxis/svelte';
90
+ import { engine, Increment } from './engine';
91
+
92
+ const store = createPraxisStore(engine);
93
+ </script>
94
+
95
+ <div>
96
+ <h2>Full State Access</h2>
97
+ <p>Count: {$store.context.count}</p>
98
+ <p>Facts: {$store.facts.length}</p>
99
+ <p>Protocol: {$store.protocolVersion}</p>
100
+
101
+ <button onclick={() => store.dispatch([Increment.create({})])}>
102
+ Increment
103
+ </button>
104
+ </div>
105
+ ```
106
+
107
+ ### createContextStore
108
+
109
+ Creates a store that tracks only the engine context.
110
+
111
+ ```typescript
112
+ import { createContextStore } from '@plures/praxis/svelte';
113
+
114
+ const contextStore = createContextStore(engine);
115
+
116
+ // Subscribe to context changes only
117
+ contextStore.subscribe((context) => {
118
+ console.log('Context updated:', context);
119
+ });
120
+
121
+ // Dispatch events
122
+ contextStore.dispatch([MyEvent.create({})]);
123
+ ```
124
+
125
+ **Use in Svelte:**
126
+
127
+ ```svelte
128
+ <script>
129
+ import { createContextStore } from '@plures/praxis/svelte';
130
+ import { engine, UpdateUser } from './engine';
131
+
132
+ const context = createContextStore(engine);
133
+ </script>
134
+
135
+ <div>
136
+ <h2>User: {$context.user?.name || 'Guest'}</h2>
137
+ <p>Page: {$context.currentPage}</p>
138
+
139
+ <button onclick={() => context.dispatch([UpdateUser.create({ name: 'Alice' })])}>
140
+ Update User
141
+ </button>
142
+ </div>
143
+ ```
144
+
145
+ ### createDerivedStore
146
+
147
+ Creates a store that extracts and tracks a specific value from the context.
148
+
149
+ ```typescript
150
+ import { createDerivedStore } from '@plures/praxis/svelte';
151
+
152
+ const countStore = createDerivedStore(
153
+ engine,
154
+ (context) => context.count
155
+ );
156
+
157
+ // Only updates when the derived value changes
158
+ countStore.subscribe((count) => {
159
+ console.log('Count:', count);
160
+ });
161
+
162
+ countStore.dispatch([Increment.create({})]);
163
+ ```
164
+
165
+ **Use in Svelte:**
166
+
167
+ ```svelte
168
+ <script>
169
+ import { createDerivedStore } from '@plures/praxis/svelte';
170
+ import { engine, Increment } from './engine';
171
+
172
+ const count = createDerivedStore(engine, (ctx) => ctx.count);
173
+ const userName = createDerivedStore(engine, (ctx) => ctx.user?.name);
174
+ </script>
175
+
176
+ <div>
177
+ <p>Count: {$count}</p>
178
+ <p>User: {$userName || 'Anonymous'}</p>
179
+
180
+ <button onclick={() => count.dispatch([Increment.create({})])}>
181
+ Increment
182
+ </button>
183
+ </div>
184
+ ```
185
+
186
+ **Performance Note:** `createDerivedStore` only notifies subscribers when the selected value actually changes, making it more efficient than subscribing to the full state.
187
+
188
+ ## Runes API (Svelte 5)
189
+
190
+ ### usePraxisEngine
191
+
192
+ The main composable for Svelte 5 runes. Provides reactive access to engine state with optional history tracking.
193
+
194
+ ```typescript
195
+ import { usePraxisEngine } from '@plures/praxis/svelte';
196
+
197
+ const {
198
+ state, // Full state (reactive)
199
+ context, // Context (reactive)
200
+ facts, // Facts array (reactive)
201
+ dispatch, // Dispatch events
202
+ // History features (when enableHistory: true)
203
+ snapshots, // Array of state snapshots
204
+ undo, // Undo last action
205
+ redo, // Redo action
206
+ canUndo, // Boolean: can undo?
207
+ canRedo, // Boolean: can redo?
208
+ historyIndex, // Current position in history
209
+ goToSnapshot, // Jump to specific snapshot
210
+ } = usePraxisEngine(engine, {
211
+ enableHistory: true,
212
+ maxHistorySize: 50,
213
+ });
214
+ ```
215
+
216
+ **Basic Example:**
217
+
218
+ ```svelte
219
+ <script lang="ts">
220
+ import { usePraxisEngine } from '@plures/praxis/svelte';
221
+ import { createCounterEngine, Increment, Decrement } from './counter';
222
+
223
+ const engine = createCounterEngine();
224
+ const { context, dispatch } = usePraxisEngine(engine);
225
+ </script>
226
+
227
+ <div>
228
+ <h1>Counter: {context.count}</h1>
229
+ <button onclick={() => dispatch([Increment.create({})])}>+</button>
230
+ <button onclick={() => dispatch([Decrement.create({})])}>-</button>
231
+ </div>
232
+ ```
233
+
234
+ **With History:**
235
+
236
+ ```svelte
237
+ <script lang="ts">
238
+ import { usePraxisEngine } from '@plures/praxis/svelte';
239
+ import { createCounterEngine, Increment } from './counter';
240
+
241
+ const engine = createCounterEngine();
242
+ const {
243
+ context,
244
+ dispatch,
245
+ undo,
246
+ redo,
247
+ canUndo,
248
+ canRedo,
249
+ snapshots,
250
+ historyIndex
251
+ } = usePraxisEngine(engine, {
252
+ enableHistory: true,
253
+ maxHistorySize: 50
254
+ });
255
+ </script>
256
+
257
+ <div class="counter-app">
258
+ <header>
259
+ <h1>Counter: {context.count}</h1>
260
+ <p>History: {historyIndex + 1} / {snapshots.length}</p>
261
+ </header>
262
+
263
+ <main>
264
+ <button onclick={() => dispatch([Increment.create({ amount: 1 })])}>
265
+ +1
266
+ </button>
267
+ <button onclick={() => dispatch([Increment.create({ amount: 5 })])}>
268
+ +5
269
+ </button>
270
+ </main>
271
+
272
+ <footer>
273
+ <button onclick={undo} disabled={!canUndo}>
274
+ ⟲ Undo
275
+ </button>
276
+ <button onclick={redo} disabled={!canRedo}>
277
+ ⟳ Redo
278
+ </button>
279
+ </footer>
280
+ </div>
281
+ ```
282
+
283
+ ### usePraxisContext
284
+
285
+ Extract a specific value from the engine context.
286
+
287
+ ```typescript
288
+ import { usePraxisContext } from '@plures/praxis/svelte';
289
+
290
+ const count = usePraxisContext(engine, (ctx) => ctx.count);
291
+ const userName = usePraxisContext(engine, (ctx) => ctx.user?.name);
292
+ ```
293
+
294
+ **Example:**
295
+
296
+ ```svelte
297
+ <script lang="ts">
298
+ import { usePraxisContext } from '@plures/praxis/svelte';
299
+ import { engine } from './engine';
300
+
301
+ const count = usePraxisContext(engine, (ctx) => ctx.count);
302
+ const isAuthenticated = usePraxisContext(engine, (ctx) => !!ctx.user);
303
+ </script>
304
+
305
+ <div>
306
+ <p>Count: {count}</p>
307
+ <p>Authenticated: {isAuthenticated}</p>
308
+ </div>
309
+ ```
310
+
311
+ ### usePraxisSubscription
312
+
313
+ Subscribe to engine state changes with automatic cleanup.
314
+
315
+ ```typescript
316
+ import { usePraxisSubscription } from '@plures/praxis/svelte';
317
+
318
+ // Automatically cleaned up when component unmounts
319
+ usePraxisSubscription(engine, (state) => {
320
+ console.log('State changed:', state);
321
+ });
322
+ ```
323
+
324
+ **Example:**
325
+
326
+ ```svelte
327
+ <script lang="ts">
328
+ import { usePraxisSubscription } from '@plures/praxis/svelte';
329
+ import { engine } from './engine';
330
+
331
+ // Log all state changes
332
+ usePraxisSubscription(engine, (state) => {
333
+ console.log('Context:', state.context);
334
+ console.log('Facts:', state.facts.length);
335
+ });
336
+
337
+ // Persist to localStorage
338
+ usePraxisSubscription(engine, (state) => {
339
+ localStorage.setItem('app-state', JSON.stringify(state.context));
340
+ });
341
+ </script>
342
+ ```
343
+
344
+ ## History & Time-Travel
345
+
346
+ ### Time-Travel Debugging
347
+
348
+ ```svelte
349
+ <script lang="ts">
350
+ import { usePraxisEngine } from '@plures/praxis/svelte';
351
+ import { createMyEngine } from './engine';
352
+
353
+ const engine = createMyEngine();
354
+ const {
355
+ context,
356
+ snapshots,
357
+ goToSnapshot,
358
+ historyIndex
359
+ } = usePraxisEngine(engine, { enableHistory: true });
360
+ </script>
361
+
362
+ <div class="debugger">
363
+ <h2>Time-Travel Debugger</h2>
364
+
365
+ <div class="timeline">
366
+ {#each snapshots as snapshot, index}
367
+ <button
368
+ class:active={index === historyIndex}
369
+ onclick={() => goToSnapshot(index)}
370
+ >
371
+ {new Date(snapshot.timestamp).toLocaleTimeString()}
372
+ <br />
373
+ {snapshot.events.length} events
374
+ </button>
375
+ {/each}
376
+ </div>
377
+
378
+ <div class="state-viewer">
379
+ <h3>State at {new Date(snapshots[historyIndex]?.timestamp).toLocaleString()}</h3>
380
+ <pre>{JSON.stringify(context, null, 2)}</pre>
381
+ </div>
382
+ </div>
383
+
384
+ <style>
385
+ .timeline {
386
+ display: flex;
387
+ gap: 0.5rem;
388
+ overflow-x: auto;
389
+ }
390
+
391
+ .timeline button {
392
+ padding: 0.5rem;
393
+ border: 2px solid #ccc;
394
+ }
395
+
396
+ .timeline button.active {
397
+ border-color: #007bff;
398
+ background: #e7f3ff;
399
+ }
400
+ </style>
401
+ ```
402
+
403
+ ### Undo/Redo UI
404
+
405
+ ```svelte
406
+ <script lang="ts">
407
+ import { usePraxisEngine } from '@plures/praxis/svelte';
408
+ import { createTextEngine, InsertText, DeleteText } from './text-engine';
409
+
410
+ const engine = createTextEngine();
411
+ const { context, dispatch, undo, redo, canUndo, canRedo } =
412
+ usePraxisEngine(engine, { enableHistory: true });
413
+
414
+ let input = '';
415
+
416
+ function handleInsert() {
417
+ if (input.trim()) {
418
+ dispatch([InsertText.create({ text: input })], 'Insert Text');
419
+ input = '';
420
+ }
421
+ }
422
+ </script>
423
+
424
+ <div class="text-editor">
425
+ <div class="toolbar">
426
+ <button onclick={undo} disabled={!canUndo} title="Undo (Ctrl+Z)">
427
+ ⟲ Undo
428
+ </button>
429
+ <button onclick={redo} disabled={!canRedo} title="Redo (Ctrl+Y)">
430
+ ⟳ Redo
431
+ </button>
432
+ </div>
433
+
434
+ <div class="editor">
435
+ <textarea bind:value={context.content} readonly></textarea>
436
+ </div>
437
+
438
+ <div class="input">
439
+ <input
440
+ type="text"
441
+ bind:value={input}
442
+ onkeypress={(e) => e.key === 'Enter' && handleInsert()}
443
+ placeholder="Type text..."
444
+ />
445
+ <button onclick={handleInsert}>Insert</button>
446
+ </div>
447
+ </div>
448
+ ```
449
+
450
+ ## Advanced Patterns
451
+
452
+ ### Multiple Engines
453
+
454
+ ```svelte
455
+ <script lang="ts">
456
+ import { usePraxisEngine } from '@plures/praxis/svelte';
457
+ import { createAuthEngine, createCartEngine } from './engines';
458
+
459
+ const authEngine = createAuthEngine();
460
+ const cartEngine = createCartEngine();
461
+
462
+ const auth = usePraxisEngine(authEngine);
463
+ const cart = usePraxisEngine(cartEngine);
464
+ </script>
465
+
466
+ <div>
467
+ <header>
468
+ {#if auth.context.user}
469
+ <p>Welcome, {auth.context.user.name}!</p>
470
+ <button onclick={() => auth.dispatch([Logout.create({})])}>
471
+ Logout
472
+ </button>
473
+ {:else}
474
+ <button onclick={() => auth.dispatch([Login.create({})])}>
475
+ Login
476
+ </button>
477
+ {/if}
478
+ </header>
479
+
480
+ <main>
481
+ <p>Cart Items: {cart.context.items.length}</p>
482
+ <button onclick={() => cart.dispatch([AddToCart.create({ id: '1' })])}>
483
+ Add Item
484
+ </button>
485
+ </main>
486
+ </div>
487
+ ```
488
+
489
+ ### Computed Values
490
+
491
+ ```svelte
492
+ <script lang="ts">
493
+ import { usePraxisEngine } from '@plures/praxis/svelte';
494
+ import { createCartEngine } from './cart-engine';
495
+
496
+ const engine = createCartEngine();
497
+ const { context, dispatch } = usePraxisEngine(engine);
498
+
499
+ // Derive computed values
500
+ $: total = context.items.reduce((sum, item) => sum + item.price, 0);
501
+ $: itemCount = context.items.length;
502
+ $: isEmpty = itemCount === 0;
503
+ </script>
504
+
505
+ <div class="cart">
506
+ <h2>Shopping Cart</h2>
507
+
508
+ {#if isEmpty}
509
+ <p>Your cart is empty</p>
510
+ {:else}
511
+ <ul>
512
+ {#each context.items as item}
513
+ <li>{item.name} - ${item.price}</li>
514
+ {/each}
515
+ </ul>
516
+
517
+ <div class="summary">
518
+ <p>Items: {itemCount}</p>
519
+ <p>Total: ${total.toFixed(2)}</p>
520
+ </div>
521
+ {/if}
522
+ </div>
523
+ ```
524
+
525
+ ### Side Effects
526
+
527
+ ```svelte
528
+ <script lang="ts">
529
+ import { usePraxisEngine, usePraxisSubscription } from '@plures/praxis/svelte';
530
+ import { createOrderEngine } from './order-engine';
531
+
532
+ const engine = createOrderEngine();
533
+ const { context, dispatch } = usePraxisEngine(engine);
534
+
535
+ // Persist to localStorage
536
+ usePraxisSubscription(engine, (state) => {
537
+ localStorage.setItem('orders', JSON.stringify(state.context.orders));
538
+ });
539
+
540
+ // Send analytics
541
+ usePraxisSubscription(engine, (state) => {
542
+ if (state.facts.some(f => f.tag === 'OrderPlaced')) {
543
+ analytics.track('order_placed', {
544
+ orderId: state.context.lastOrderId,
545
+ });
546
+ }
547
+ });
548
+
549
+ // Show notifications
550
+ usePraxisSubscription(engine, (state) => {
551
+ const errors = state.facts.filter(f => f.tag === 'OrderFailed');
552
+ if (errors.length > 0) {
553
+ const error = errors[errors.length - 1];
554
+ showNotification('Order Failed', error.payload.reason);
555
+ }
556
+ });
557
+ </script>
558
+ ```
559
+
560
+ ## Performance Tips
561
+
562
+ ### 1. Use Derived Stores for Specific Values
563
+
564
+ ```svelte
565
+ <!-- ❌ Less efficient: subscribes to all state changes -->
566
+ <script>
567
+ const store = createPraxisStore(engine);
568
+ </script>
569
+ <p>Count: {$store.context.count}</p>
570
+
571
+ <!-- ✅ More efficient: only updates when count changes -->
572
+ <script>
573
+ const count = createDerivedStore(engine, ctx => ctx.count);
574
+ </script>
575
+ <p>Count: {$count}</p>
576
+ ```
577
+
578
+ ### 2. Limit History Size
579
+
580
+ ```typescript
581
+ // For user-facing features
582
+ usePraxisEngine(engine, {
583
+ enableHistory: true,
584
+ maxHistorySize: 20 // Smaller for better performance
585
+ });
586
+
587
+ // For debugging/development
588
+ usePraxisEngine(engine, {
589
+ enableHistory: true,
590
+ maxHistorySize: 100 // Larger for more history
591
+ });
592
+ ```
593
+
594
+ ### 3. Batch Events
595
+
596
+ ```typescript
597
+ // ❌ Multiple dispatches
598
+ dispatch([Increment.create({})]);
599
+ dispatch([UpdateUser.create({ name: 'Alice' })]);
600
+ dispatch([SaveData.create({})]);
601
+
602
+ // ✅ Single dispatch
603
+ dispatch([
604
+ Increment.create({}),
605
+ UpdateUser.create({ name: 'Alice' }),
606
+ SaveData.create({})
607
+ ]);
608
+ ```
609
+
610
+ ### 4. Memoize Selectors
611
+
612
+ ```typescript
613
+ // ❌ New function on every render
614
+ const count = usePraxisContext(engine, (ctx) => ctx.count);
615
+
616
+ // ✅ Reuse selector function
617
+ const countSelector = (ctx: MyContext) => ctx.count;
618
+ const count = usePraxisContext(engine, countSelector);
619
+ ```
620
+
621
+ ## TypeScript Support
622
+
623
+ All APIs are fully typed:
624
+
625
+ ```typescript
626
+ import type {
627
+ LogicEngine,
628
+ Readable,
629
+ Writable,
630
+ StateSnapshot,
631
+ HistoryEntry,
632
+ PraxisEngineBinding,
633
+ } from '@plures/praxis/svelte';
634
+
635
+ interface MyContext {
636
+ count: number;
637
+ user: { id: string; name: string } | null;
638
+ }
639
+
640
+ const engine: LogicEngine<MyContext> = createPraxisEngine({ /* ... */ });
641
+
642
+ // Store types are inferred
643
+ const store: Readable<PraxisState & { context: MyContext }> & {
644
+ dispatch: (events: PraxisEvent[]) => void;
645
+ } = createPraxisStore(engine);
646
+
647
+ // Runes types are inferred
648
+ const binding: PraxisEngineBinding<MyContext> = usePraxisEngine(engine, {
649
+ enableHistory: true
650
+ });
651
+
652
+ // Selector types are inferred
653
+ const count: number = usePraxisContext(
654
+ engine,
655
+ (ctx: MyContext) => ctx.count
656
+ );
657
+ ```
658
+
659
+ ## Migration from XState
660
+
661
+ If you're coming from XState:
662
+
663
+ | XState | Praxis |
664
+ |--------|--------|
665
+ | `const [state, send] = useMachine(machine)` | `const { context, dispatch } = usePraxisEngine(engine)` |
666
+ | `state.context.count` | `context.count` |
667
+ | `send({ type: 'INCREMENT' })` | `dispatch([Increment.create({})])` |
668
+ | `state.matches('idle')` | Check context: `context.status === 'idle'` |
669
+ | `state.history` | `usePraxisEngine(engine, { enableHistory: true })` |
670
+ | `const service = interpret(machine)` | `const store = createPraxisStore(engine)` |
671
+
672
+ ## Examples
673
+
674
+ See the following examples for complete implementations:
675
+
676
+ - [Counter with Svelte 5](/src/examples/svelte-counter/index.ts)
677
+ - [Auth Flow with History](/docs/guides/history-state-pattern.md#authentication-flow-example)
678
+ - [Svelte Integration Tests](/src/__tests__/svelte-integration.test.ts)
679
+
680
+ ## Summary
681
+
682
+ Praxis provides comprehensive Svelte 5 support through:
683
+
684
+ - ✅ **Traditional Stores**: Compatible with Svelte 4 and 5
685
+ - ✅ **Modern Runes**: First-class Svelte 5 runes support
686
+ - ✅ **History Tracking**: Built-in undo/redo and time-travel
687
+ - ✅ **Type Safety**: Full TypeScript support throughout
688
+ - ✅ **Performance**: Efficient updates with derived stores
689
+ - ✅ **Developer Experience**: Simple, intuitive API
690
+
691
+ Whether you prefer stores or runes, Praxis has you covered!