@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,371 @@
1
+ # Advanced Todo App with Praxis
2
+
3
+ A comprehensive example demonstrating Praxis's Svelte 5 integration with history state pattern.
4
+
5
+ ## Features
6
+
7
+ ### Core Functionality
8
+ - ✅ Add, complete, and remove todos
9
+ - ✅ Filter by all, active, or completed
10
+ - ✅ Complete all todos at once
11
+ - ✅ Clear completed todos
12
+ - ✅ Real-time statistics
13
+
14
+ ### Advanced Features
15
+ - 🔄 **Undo/Redo**: Full history tracking with keyboard shortcuts
16
+ - 🕰️ **Time-Travel Debugging**: Navigate through state snapshots
17
+ - ⚡ **Svelte 5 Runes**: Modern reactive API
18
+ - 🎨 **Beautiful UI**: Clean, intuitive interface
19
+ - ⌨️ **Keyboard Shortcuts**: Efficient workflow
20
+
21
+ ## Architecture
22
+
23
+ ### State Management
24
+ - **Engine**: Praxis logic engine managing todo state
25
+ - **Rules**: Pure functions handling events
26
+ - **Facts**: Immutable history of what happened
27
+ - **Context**: Current application state
28
+
29
+ ### Svelte Integration
30
+ - **usePraxisEngine**: Main composable with history support
31
+ - **Reactive Derivations**: Computed values with `$:` syntax
32
+ - **Event Dispatching**: Type-safe event handling
33
+ - **Snapshot Navigation**: Time-travel through history
34
+
35
+ ## Code Structure
36
+
37
+ ```
38
+ advanced-todo/
39
+ ├── index.ts # Engine definition, rules, and logic
40
+ ├── App.svelte # Svelte 5 component with UI
41
+ └── README.md # This file
42
+ ```
43
+
44
+ ### Engine Definition (`index.ts`)
45
+
46
+ ```typescript
47
+ // Define context type
48
+ interface TodoContext {
49
+ todos: TodoItem[];
50
+ filter: 'all' | 'active' | 'completed';
51
+ nextId: number;
52
+ }
53
+
54
+ // Define events
55
+ const AddTodo = defineEvent<'ADD_TODO', { text: string }>('ADD_TODO');
56
+ const ToggleTodo = defineEvent<'TOGGLE_TODO', { id: string }>('TOGGLE_TODO');
57
+ // ... more events
58
+
59
+ // Define rules
60
+ const addTodoRule = defineRule<TodoContext>({
61
+ id: 'todo.add',
62
+ description: 'Add a new todo item',
63
+ impl: (state, events) => {
64
+ // Pure function: no side effects
65
+ // Returns facts about what happened
66
+ },
67
+ });
68
+
69
+ // Create engine
70
+ export function createTodoEngine() {
71
+ const registry = new PraxisRegistry<TodoContext>();
72
+ registry.registerRule(addTodoRule);
73
+ // ... register more rules
74
+
75
+ return createPraxisEngine({
76
+ initialContext: { todos: [], filter: 'all', nextId: 1 },
77
+ registry,
78
+ });
79
+ }
80
+ ```
81
+
82
+ ### Svelte Component (`App.svelte`)
83
+
84
+ ```svelte
85
+ <script lang="ts">
86
+ import { usePraxisEngine } from '@plures/praxis/svelte';
87
+ import { createTodoEngine, AddTodo } from './index';
88
+
89
+ const engine = createTodoEngine();
90
+ const {
91
+ context, // Reactive context
92
+ dispatch, // Dispatch events
93
+ undo, // Undo last action
94
+ redo, // Redo action
95
+ canUndo, // Boolean: can undo?
96
+ canRedo, // Boolean: can redo?
97
+ snapshots, // History snapshots
98
+ historyIndex, // Current position
99
+ goToSnapshot, // Jump to snapshot
100
+ } = usePraxisEngine(engine, {
101
+ enableHistory: true,
102
+ maxHistorySize: 50,
103
+ });
104
+
105
+ function handleAddTodo(text: string) {
106
+ dispatch([AddTodo.create({ text })], 'Add Todo');
107
+ }
108
+ </script>
109
+
110
+ <div>
111
+ <p>Todos: {context.todos.length}</p>
112
+ <button onclick={undo} disabled={!canUndo}>Undo</button>
113
+ <button onclick={redo} disabled={!canRedo}>Redo</button>
114
+ </div>
115
+ ```
116
+
117
+ ## Running the Example
118
+
119
+ ### Non-Svelte Version
120
+
121
+ Run the TypeScript example directly:
122
+
123
+ ```bash
124
+ cd /home/runner/work/praxis/praxis
125
+ npm run build
126
+ node dist/examples/advanced-todo/index.js
127
+ ```
128
+
129
+ Output:
130
+ ```
131
+ === Advanced Todo Example ===
132
+
133
+ 1. Adding todos:
134
+ Total todos: 3
135
+ - Learn Praxis
136
+ - Build awesome app
137
+ - Deploy to production
138
+
139
+ 2. Completing first todo:
140
+ Active: 2, Completed: 1
141
+
142
+ 3. Filtering to active todos:
143
+ Filter: active
144
+ Showing 2 todos:
145
+ - Build awesome app
146
+ - Deploy to production
147
+
148
+ 4. Completing all todos:
149
+ All completed: 3/3
150
+
151
+ 5. Clearing completed todos:
152
+ Remaining todos: 0
153
+ ```
154
+
155
+ ### Svelte 5 Version
156
+
157
+ To use the Svelte component in your app:
158
+
159
+ 1. Install dependencies:
160
+ ```bash
161
+ npm install @plures/praxis svelte@^5.0.0
162
+ ```
163
+
164
+ 2. Import and use the component:
165
+ ```svelte
166
+ <script>
167
+ import TodoApp from '@plures/praxis/examples/advanced-todo/App.svelte';
168
+ </script>
169
+
170
+ <TodoApp />
171
+ ```
172
+
173
+ Or create your own component using the engine:
174
+
175
+ ```svelte
176
+ <script lang="ts">
177
+ import { usePraxisEngine } from '@plures/praxis/svelte';
178
+ import { createTodoEngine, AddTodo } from '@plures/praxis/examples/advanced-todo';
179
+
180
+ const engine = createTodoEngine();
181
+ const { context, dispatch } = usePraxisEngine(engine);
182
+
183
+ let text = '';
184
+ </script>
185
+
186
+ <input bind:value={text} />
187
+ <button onclick={() => dispatch([AddTodo.create({ text })])}>
188
+ Add
189
+ </button>
190
+
191
+ <ul>
192
+ {#each context.todos as todo}
193
+ <li>{todo.text}</li>
194
+ {/each}
195
+ </ul>
196
+ ```
197
+
198
+ ## Keyboard Shortcuts
199
+
200
+ - **Ctrl+Z** / **Cmd+Z**: Undo
201
+ - **Ctrl+Y** / **Cmd+Y**: Redo
202
+ - **Ctrl+D** / **Cmd+D**: Toggle debugger
203
+ - **Enter**: Add todo (when input is focused)
204
+
205
+ ## Key Concepts
206
+
207
+ ### 1. Immutable State Updates
208
+
209
+ ```typescript
210
+ // ❌ Don't mutate directly (outside rules)
211
+ context.todos.push(newTodo);
212
+
213
+ // ✅ Do dispatch events
214
+ dispatch([AddTodo.create({ text: 'New todo' })]);
215
+ ```
216
+
217
+ ### 2. Pure Rules
218
+
219
+ ```typescript
220
+ const addTodoRule = defineRule({
221
+ id: 'todo.add',
222
+ impl: (state, events) => {
223
+ // ✅ Pure function
224
+ // - Deterministic
225
+ // - No side effects
226
+ // - Testable
227
+ const event = findEvent(events, AddTodo);
228
+ if (!event) return [];
229
+
230
+ state.context.todos.push({ /* ... */ });
231
+ return [TodoAdded.create({ /* ... */ })];
232
+ },
233
+ });
234
+ ```
235
+
236
+ ### 3. Facts as History
237
+
238
+ ```typescript
239
+ // Facts are immutable records of what happened
240
+ const facts = engine.getFacts();
241
+
242
+ // Find specific facts
243
+ const added = facts.filter(f => f.tag === 'TodoAdded');
244
+ const toggled = facts.filter(f => f.tag === 'TodoToggled');
245
+
246
+ // Facts enable:
247
+ // - Audit trails
248
+ // - Event sourcing
249
+ // - Time-travel debugging
250
+ ```
251
+
252
+ ### 4. Reactive Derivations
253
+
254
+ ```svelte
255
+ <script>
256
+ // Svelte automatically tracks dependencies
257
+ $: filteredTodos = getFilteredTodos(context.todos, context.filter);
258
+ $: stats = getStats(context.todos);
259
+ $: hasCompleted = stats.completed > 0;
260
+ </script>
261
+ ```
262
+
263
+ ### 5. History State Pattern
264
+
265
+ ```typescript
266
+ // Enable history tracking
267
+ const { undo, redo, snapshots, goToSnapshot } = usePraxisEngine(engine, {
268
+ enableHistory: true,
269
+ maxHistorySize: 50,
270
+ });
271
+
272
+ // Navigate history
273
+ undo(); // Go back
274
+ redo(); // Go forward
275
+ goToSnapshot(5); // Jump to specific snapshot
276
+ console.log(snapshots); // View all snapshots
277
+ ```
278
+
279
+ ## Testing
280
+
281
+ The engine can be tested independently of Svelte:
282
+
283
+ ```typescript
284
+ import { createTodoEngine, AddTodo, ToggleTodo } from './index';
285
+
286
+ describe('Todo Engine', () => {
287
+ it('should add todos', () => {
288
+ const engine = createTodoEngine();
289
+ engine.step([AddTodo.create({ text: 'Test todo' })]);
290
+
291
+ const context = engine.getContext();
292
+ expect(context.todos.length).toBe(1);
293
+ expect(context.todos[0].text).toBe('Test todo');
294
+ });
295
+
296
+ it('should toggle todos', () => {
297
+ const engine = createTodoEngine();
298
+ engine.step([AddTodo.create({ text: 'Test' })]);
299
+
300
+ const context = engine.getContext();
301
+ const todoId = context.todos[0].id;
302
+
303
+ engine.step([ToggleTodo.create({ id: todoId })]);
304
+ expect(engine.getContext().todos[0].completed).toBe(true);
305
+ });
306
+ });
307
+ ```
308
+
309
+ ## Performance Tips
310
+
311
+ 1. **Batch Events**: Dispatch multiple events at once
312
+ ```typescript
313
+ dispatch([
314
+ AddTodo.create({ text: 'Todo 1' }),
315
+ AddTodo.create({ text: 'Todo 2' }),
316
+ AddTodo.create({ text: 'Todo 3' }),
317
+ ], 'Batch Add');
318
+ ```
319
+
320
+ 2. **Limit History**: Adjust history size based on needs
321
+ ```typescript
322
+ usePraxisEngine(engine, { maxHistorySize: 20 });
323
+ ```
324
+
325
+ 3. **Selective Subscriptions**: Use derived stores for specific values
326
+ ```typescript
327
+ const count = createDerivedStore(engine, ctx => ctx.todos.length);
328
+ ```
329
+
330
+ ## Comparison with Other Libraries
331
+
332
+ ### vs. Redux + Redux Toolkit
333
+
334
+ | Feature | Redux + RTK | Praxis |
335
+ |---------|-------------|--------|
336
+ | State Updates | Reducers | Rules |
337
+ | Side Effects | Thunks/Sagas | Actors |
338
+ | Time-Travel | DevTools | Built-in |
339
+ | TypeScript | Good | Excellent |
340
+ | Learning Curve | Medium | Low |
341
+
342
+ ### vs. XState
343
+
344
+ | Feature | XState | Praxis |
345
+ |---------|--------|--------|
346
+ | State Machines | Native | Engine-based |
347
+ | Svelte Integration | @xstate/svelte | @plures/praxis/svelte |
348
+ | History States | Built-in | Pattern-based |
349
+ | Visual Tools | Stately | State-Docs (planned) |
350
+ | Learning Curve | High | Medium |
351
+
352
+ ### vs. Svelte Stores
353
+
354
+ | Feature | Svelte Stores | Praxis |
355
+ |---------|---------------|--------|
356
+ | Simplicity | Very High | High |
357
+ | Structure | Manual | Built-in |
358
+ | Time-Travel | Manual | Built-in |
359
+ | Rules/Logic | Manual | Built-in |
360
+ | TypeScript | Good | Excellent |
361
+
362
+ ## Next Steps
363
+
364
+ - Explore [History State Pattern Guide](../../docs/guides/history-state-pattern.md)
365
+ - Read [Svelte Integration Guide](../../docs/guides/svelte-integration.md)
366
+ - Check [Parallel State Pattern](../../docs/guides/parallel-state-pattern.md)
367
+ - View [More Examples](../)
368
+
369
+ ## License
370
+
371
+ MIT
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Advanced Svelte 5 Integration Example
3
+ *
4
+ * Demonstrates the new Svelte 5 runes support with history state pattern.
5
+ * This example shows a todo app with undo/redo and time-travel debugging.
6
+ */
7
+
8
+ import {
9
+ createPraxisEngine,
10
+ PraxisRegistry,
11
+ defineFact,
12
+ defineEvent,
13
+ defineRule,
14
+ findEvent,
15
+ } from '../../index.js';
16
+
17
+ // ============================================================================
18
+ // Context & Domain Types
19
+ // ============================================================================
20
+
21
+ interface TodoItem {
22
+ id: string;
23
+ text: string;
24
+ completed: boolean;
25
+ createdAt: number;
26
+ }
27
+
28
+ interface TodoContext {
29
+ todos: TodoItem[];
30
+ filter: 'all' | 'active' | 'completed';
31
+ nextId: number;
32
+ }
33
+
34
+ // ============================================================================
35
+ // Facts
36
+ // ============================================================================
37
+
38
+ const TodoAdded = defineFact<'TodoAdded', { id: string; text: string }>('TodoAdded');
39
+ const TodoToggled = defineFact<'TodoToggled', { id: string }>('TodoToggled');
40
+ const TodoRemoved = defineFact<'TodoRemoved', { id: string }>('TodoRemoved');
41
+ const FilterChanged = defineFact<'FilterChanged', { filter: string }>('FilterChanged');
42
+ const AllCompleted = defineFact<'AllCompleted', {}>('AllCompleted');
43
+ const CompletedCleared = defineFact<'CompletedCleared', {}>('CompletedCleared');
44
+
45
+ // ============================================================================
46
+ // Events
47
+ // ============================================================================
48
+
49
+ const AddTodo = defineEvent<'ADD_TODO', { text: string }>('ADD_TODO');
50
+ const ToggleTodo = defineEvent<'TOGGLE_TODO', { id: string }>('TOGGLE_TODO');
51
+ const RemoveTodo = defineEvent<'REMOVE_TODO', { id: string }>('REMOVE_TODO');
52
+ const SetFilter = defineEvent<'SET_FILTER', { filter: 'all' | 'active' | 'completed' }>(
53
+ 'SET_FILTER'
54
+ );
55
+ const CompleteAll = defineEvent<'COMPLETE_ALL', {}>('COMPLETE_ALL');
56
+ const ClearCompleted = defineEvent<'CLEAR_COMPLETED', {}>('CLEAR_COMPLETED');
57
+
58
+ // ============================================================================
59
+ // Rules
60
+ // ============================================================================
61
+
62
+ const addTodoRule = defineRule<TodoContext>({
63
+ id: 'todo.add',
64
+ description: 'Add a new todo item',
65
+ impl: (state, events) => {
66
+ const event = findEvent(events, AddTodo);
67
+ if (!event || !event.payload.text.trim()) {
68
+ return [];
69
+ }
70
+
71
+ const id = `todo-${state.context.nextId}`;
72
+ const todo: TodoItem = {
73
+ id,
74
+ text: event.payload.text.trim(),
75
+ completed: false,
76
+ createdAt: Date.now(),
77
+ };
78
+
79
+ state.context.todos.push(todo);
80
+ state.context.nextId++;
81
+
82
+ return [TodoAdded.create({ id, text: todo.text })];
83
+ },
84
+ });
85
+
86
+ const toggleTodoRule = defineRule<TodoContext>({
87
+ id: 'todo.toggle',
88
+ description: 'Toggle todo completion status',
89
+ impl: (state, events) => {
90
+ const event = findEvent(events, ToggleTodo);
91
+ if (!event) {
92
+ return [];
93
+ }
94
+
95
+ const todo = state.context.todos.find((t) => t.id === event.payload.id);
96
+ if (todo) {
97
+ todo.completed = !todo.completed;
98
+ return [TodoToggled.create({ id: todo.id })];
99
+ }
100
+
101
+ return [];
102
+ },
103
+ });
104
+
105
+ const removeTodoRule = defineRule<TodoContext>({
106
+ id: 'todo.remove',
107
+ description: 'Remove a todo item',
108
+ impl: (state, events) => {
109
+ const event = findEvent(events, RemoveTodo);
110
+ if (!event) {
111
+ return [];
112
+ }
113
+
114
+ const index = state.context.todos.findIndex((t) => t.id === event.payload.id);
115
+ if (index !== -1) {
116
+ state.context.todos.splice(index, 1);
117
+ return [TodoRemoved.create({ id: event.payload.id })];
118
+ }
119
+
120
+ return [];
121
+ },
122
+ });
123
+
124
+ const setFilterRule = defineRule<TodoContext>({
125
+ id: 'todo.setFilter',
126
+ description: 'Change the filter',
127
+ impl: (state, events) => {
128
+ const event = findEvent(events, SetFilter);
129
+ if (!event) {
130
+ return [];
131
+ }
132
+
133
+ state.context.filter = event.payload.filter;
134
+ return [FilterChanged.create({ filter: event.payload.filter })];
135
+ },
136
+ });
137
+
138
+ const completeAllRule = defineRule<TodoContext>({
139
+ id: 'todo.completeAll',
140
+ description: 'Mark all todos as completed',
141
+ impl: (state, events) => {
142
+ const event = findEvent(events, CompleteAll);
143
+ if (!event) {
144
+ return [];
145
+ }
146
+
147
+ state.context.todos.forEach((todo) => {
148
+ todo.completed = true;
149
+ });
150
+
151
+ return [AllCompleted.create({})];
152
+ },
153
+ });
154
+
155
+ const clearCompletedRule = defineRule<TodoContext>({
156
+ id: 'todo.clearCompleted',
157
+ description: 'Remove all completed todos',
158
+ impl: (state, events) => {
159
+ const event = findEvent(events, ClearCompleted);
160
+ if (!event) {
161
+ return [];
162
+ }
163
+
164
+ const beforeCount = state.context.todos.length;
165
+ state.context.todos = state.context.todos.filter((t) => !t.completed);
166
+ const afterCount = state.context.todos.length;
167
+
168
+ if (beforeCount !== afterCount) {
169
+ return [CompletedCleared.create({})];
170
+ }
171
+
172
+ return [];
173
+ },
174
+ });
175
+
176
+ // ============================================================================
177
+ // Engine Factory
178
+ // ============================================================================
179
+
180
+ export function createTodoEngine() {
181
+ const registry = new PraxisRegistry<TodoContext>();
182
+
183
+ registry.registerRule(addTodoRule);
184
+ registry.registerRule(toggleTodoRule);
185
+ registry.registerRule(removeTodoRule);
186
+ registry.registerRule(setFilterRule);
187
+ registry.registerRule(completeAllRule);
188
+ registry.registerRule(clearCompletedRule);
189
+
190
+ return createPraxisEngine<TodoContext>({
191
+ initialContext: {
192
+ todos: [],
193
+ filter: 'all',
194
+ nextId: 1,
195
+ },
196
+ registry,
197
+ });
198
+ }
199
+
200
+ // ============================================================================
201
+ // Helper Functions for Svelte
202
+ // ============================================================================
203
+
204
+ export function getFilteredTodos(todos: TodoItem[], filter: string): TodoItem[] {
205
+ switch (filter) {
206
+ case 'active':
207
+ return todos.filter((t) => !t.completed);
208
+ case 'completed':
209
+ return todos.filter((t) => t.completed);
210
+ default:
211
+ return todos;
212
+ }
213
+ }
214
+
215
+ export function getStats(todos: TodoItem[]) {
216
+ const total = todos.length;
217
+ const active = todos.filter((t) => !t.completed).length;
218
+ const completed = todos.filter((t) => t.completed).length;
219
+
220
+ return { total, active, completed };
221
+ }
222
+
223
+ // ============================================================================
224
+ // Example Usage (non-Svelte demonstration)
225
+ // ============================================================================
226
+
227
+ function runExample() {
228
+ console.log('=== Advanced Todo Example ===\n');
229
+
230
+ const engine = createTodoEngine();
231
+
232
+ // Add some todos
233
+ console.log('1. Adding todos:');
234
+ engine.step([AddTodo.create({ text: 'Learn Praxis' })]);
235
+ engine.step([AddTodo.create({ text: 'Build awesome app' })]);
236
+ engine.step([AddTodo.create({ text: 'Deploy to production' })]);
237
+
238
+ const context1 = engine.getContext();
239
+ console.log(` Total todos: ${context1.todos.length}`);
240
+ context1.todos.forEach((todo) => {
241
+ console.log(` - ${todo.text}`);
242
+ });
243
+ console.log();
244
+
245
+ // Toggle a todo
246
+ console.log('2. Completing first todo:');
247
+ const firstId = context1.todos[0].id;
248
+ engine.step([ToggleTodo.create({ id: firstId })]);
249
+
250
+ const context2 = engine.getContext();
251
+ const stats = getStats(context2.todos);
252
+ console.log(` Active: ${stats.active}, Completed: ${stats.completed}`);
253
+ console.log();
254
+
255
+ // Change filter
256
+ console.log('3. Filtering to active todos:');
257
+ engine.step([SetFilter.create({ filter: 'active' })])
258
+
259
+ const context3 = engine.getContext();
260
+ const filtered = getFilteredTodos(context3.todos, context3.filter);
261
+ console.log(` Filter: ${context3.filter}`);
262
+ console.log(` Showing ${filtered.length} todos:`);
263
+ filtered.forEach((todo) => {
264
+ console.log(` - ${todo.text}`);
265
+ });
266
+ console.log();
267
+
268
+ // Complete all
269
+ console.log('4. Completing all todos:');
270
+ engine.step([CompleteAll.create({})]);
271
+
272
+ const context4 = engine.getContext();
273
+ const stats4 = getStats(context4.todos);
274
+ console.log(` All completed: ${stats4.completed}/${stats4.total}`);
275
+ console.log();
276
+
277
+ // Clear completed
278
+ console.log('5. Clearing completed todos:');
279
+ engine.step([ClearCompleted.create({})]);
280
+
281
+ const context5 = engine.getContext();
282
+ console.log(` Remaining todos: ${context5.todos.length}`);
283
+ console.log();
284
+ }
285
+
286
+ // Run example if executed directly
287
+ if (import.meta.url === `file://${process.argv[1]}`) {
288
+ runExample();
289
+ }
290
+
291
+ export {
292
+ // Types
293
+ type TodoItem,
294
+ type TodoContext,
295
+ // Events
296
+ AddTodo,
297
+ ToggleTodo,
298
+ RemoveTodo,
299
+ SetFilter,
300
+ CompleteAll,
301
+ ClearCompleted,
302
+ // Facts
303
+ TodoAdded,
304
+ TodoToggled,
305
+ TodoRemoved,
306
+ FilterChanged,
307
+ AllCompleted,
308
+ CompletedCleared,
309
+ };