@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,218 @@
1
+ /**
2
+ * DSL Helpers
3
+ *
4
+ * Ergonomic TypeScript helpers for defining facts, events, rules, constraints, and flows.
5
+ * These produce both strongly-typed APIs and serializable descriptors.
6
+ */
7
+
8
+ import type { PraxisEvent, PraxisFact } from "../core/protocol.js";
9
+ import type {
10
+ RuleDescriptor,
11
+ ConstraintDescriptor,
12
+ PraxisModule,
13
+ RuleFn,
14
+ ConstraintFn,
15
+ } from "../core/rules.js";
16
+
17
+ /**
18
+ * Strongly typed fact definition
19
+ */
20
+ export interface FactDefinition<TTag extends string, TPayload> {
21
+ tag: TTag;
22
+ create(payload: TPayload): PraxisFact & { tag: TTag; payload: TPayload };
23
+ is(fact: PraxisFact): fact is PraxisFact & { tag: TTag; payload: TPayload };
24
+ }
25
+
26
+ /**
27
+ * Define a typed fact
28
+ *
29
+ * @example
30
+ * const UserLoggedIn = defineFact<"UserLoggedIn", { userId: string }>("UserLoggedIn");
31
+ * const fact = UserLoggedIn.create({ userId: "123" });
32
+ * if (UserLoggedIn.is(fact)) {
33
+ * console.log(fact.payload.userId); // Type-safe!
34
+ * }
35
+ */
36
+ export function defineFact<TTag extends string, TPayload>(
37
+ tag: TTag
38
+ ): FactDefinition<TTag, TPayload> {
39
+ return {
40
+ tag,
41
+ create(payload: TPayload): PraxisFact & { tag: TTag; payload: TPayload } {
42
+ return { tag, payload };
43
+ },
44
+ is(fact: PraxisFact): fact is PraxisFact & { tag: TTag; payload: TPayload } {
45
+ return fact.tag === tag;
46
+ },
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Strongly typed event definition
52
+ */
53
+ export interface EventDefinition<TTag extends string, TPayload> {
54
+ tag: TTag;
55
+ create(payload: TPayload): PraxisEvent & { tag: TTag; payload: TPayload };
56
+ is(event: PraxisEvent): event is PraxisEvent & { tag: TTag; payload: TPayload };
57
+ }
58
+
59
+ /**
60
+ * Define a typed event
61
+ *
62
+ * @example
63
+ * const Login = defineEvent<"LOGIN", { username: string; password: string }>("LOGIN");
64
+ * const event = Login.create({ username: "alice", password: "secret" });
65
+ */
66
+ export function defineEvent<TTag extends string, TPayload>(
67
+ tag: TTag
68
+ ): EventDefinition<TTag, TPayload> {
69
+ return {
70
+ tag,
71
+ create(payload: TPayload): PraxisEvent & { tag: TTag; payload: TPayload } {
72
+ return { tag, payload };
73
+ },
74
+ is(event: PraxisEvent): event is PraxisEvent & { tag: TTag; payload: TPayload } {
75
+ return event.tag === tag;
76
+ },
77
+ };
78
+ }
79
+
80
+ /**
81
+ * Options for defining a rule
82
+ */
83
+ export interface DefineRuleOptions<TContext = unknown> {
84
+ id: string;
85
+ description: string;
86
+ impl: RuleFn<TContext>;
87
+ meta?: Record<string, unknown>;
88
+ }
89
+
90
+ /**
91
+ * Define a rule
92
+ *
93
+ * @example
94
+ * const loginRule = defineRule({
95
+ * id: "auth.login",
96
+ * description: "Process login event",
97
+ * impl: (state, events) => {
98
+ * const loginEvent = events.find(Login.is);
99
+ * if (loginEvent) {
100
+ * return [UserLoggedIn.create({ userId: loginEvent.payload.username })];
101
+ * }
102
+ * return [];
103
+ * }
104
+ * });
105
+ */
106
+ export function defineRule<TContext = unknown>(
107
+ options: DefineRuleOptions<TContext>
108
+ ): RuleDescriptor<TContext> {
109
+ return {
110
+ id: options.id,
111
+ description: options.description,
112
+ impl: options.impl,
113
+ meta: options.meta,
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Options for defining a constraint
119
+ */
120
+ export interface DefineConstraintOptions<TContext = unknown> {
121
+ id: string;
122
+ description: string;
123
+ impl: ConstraintFn<TContext>;
124
+ meta?: Record<string, unknown>;
125
+ }
126
+
127
+ /**
128
+ * Define a constraint
129
+ *
130
+ * @example
131
+ * const maxCartItems = defineConstraint({
132
+ * id: "cart.maxItems",
133
+ * description: "Cart cannot exceed 100 items",
134
+ * impl: (state) => {
135
+ * const itemCount = state.context.items?.length ?? 0;
136
+ * return itemCount <= 100 || `Cart has ${itemCount} items, maximum is 100`;
137
+ * }
138
+ * });
139
+ */
140
+ export function defineConstraint<TContext = unknown>(
141
+ options: DefineConstraintOptions<TContext>
142
+ ): ConstraintDescriptor<TContext> {
143
+ return {
144
+ id: options.id,
145
+ description: options.description,
146
+ impl: options.impl,
147
+ meta: options.meta,
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Options for defining a module
153
+ */
154
+ export interface DefineModuleOptions<TContext = unknown> {
155
+ rules?: RuleDescriptor<TContext>[];
156
+ constraints?: ConstraintDescriptor<TContext>[];
157
+ meta?: Record<string, unknown>;
158
+ }
159
+
160
+ /**
161
+ * Define a module (bundle of rules and constraints)
162
+ *
163
+ * @example
164
+ * const authModule = defineModule({
165
+ * rules: [loginRule, logoutRule],
166
+ * constraints: [maxSessionsConstraint],
167
+ * meta: { version: "1.0.0" }
168
+ * });
169
+ */
170
+ export function defineModule<TContext = unknown>(
171
+ options: DefineModuleOptions<TContext>
172
+ ): PraxisModule<TContext> {
173
+ return {
174
+ rules: options.rules ?? [],
175
+ constraints: options.constraints ?? [],
176
+ meta: options.meta,
177
+ };
178
+ }
179
+
180
+ /**
181
+ * Helper to filter events by definition
182
+ */
183
+ export function filterEvents<TTag extends string, TPayload>(
184
+ events: PraxisEvent[],
185
+ definition: EventDefinition<TTag, TPayload>
186
+ ): Array<PraxisEvent & { tag: TTag; payload: TPayload }> {
187
+ return events.filter(definition.is);
188
+ }
189
+
190
+ /**
191
+ * Helper to filter facts by definition
192
+ */
193
+ export function filterFacts<TTag extends string, TPayload>(
194
+ facts: PraxisFact[],
195
+ definition: FactDefinition<TTag, TPayload>
196
+ ): Array<PraxisFact & { tag: TTag; payload: TPayload }> {
197
+ return facts.filter(definition.is);
198
+ }
199
+
200
+ /**
201
+ * Helper to find first event matching definition
202
+ */
203
+ export function findEvent<TTag extends string, TPayload>(
204
+ events: PraxisEvent[],
205
+ definition: EventDefinition<TTag, TPayload>
206
+ ): (PraxisEvent & { tag: TTag; payload: TPayload }) | undefined {
207
+ return events.find(definition.is);
208
+ }
209
+
210
+ /**
211
+ * Helper to find first fact matching definition
212
+ */
213
+ export function findFact<TTag extends string, TPayload>(
214
+ facts: PraxisFact[],
215
+ definition: FactDefinition<TTag, TPayload>
216
+ ): (PraxisFact & { tag: TTag; payload: TPayload }) | undefined {
217
+ return facts.find(definition.is);
218
+ }
package/src/dsl.ts ADDED
@@ -0,0 +1,214 @@
1
+ /**
2
+ * DSL for defining rules and constraints in Praxis.
3
+ * Rules are condition-action pairs that fire when conditions are met.
4
+ * Constraints are invariants that must hold true in valid states.
5
+ */
6
+
7
+ import type { PraxisState, PraxisEvent, Effect } from './types.js';
8
+
9
+ /**
10
+ * A condition function that checks if a rule should fire.
11
+ */
12
+ export type Condition<
13
+ S extends PraxisState = PraxisState,
14
+ E extends PraxisEvent = PraxisEvent
15
+ > = (state: S, event: E) => boolean;
16
+
17
+ /**
18
+ * An action function that produces effects when a rule fires.
19
+ */
20
+ export type Action<
21
+ S extends PraxisState = PraxisState,
22
+ E extends PraxisEvent = PraxisEvent
23
+ > = (state: S, event: E) => Effect[];
24
+
25
+ /**
26
+ * A rule that fires when its condition is met.
27
+ */
28
+ export interface Rule<
29
+ S extends PraxisState = PraxisState,
30
+ E extends PraxisEvent = PraxisEvent
31
+ > {
32
+ /** Unique identifier for the rule */
33
+ id: string;
34
+ /** Human-readable description of what the rule does */
35
+ description?: string;
36
+ /** Priority for rule execution (higher numbers = higher priority) */
37
+ priority?: number;
38
+ /** Condition that must be true for the rule to fire */
39
+ when: Condition<S, E>;
40
+ /** Action to execute when the rule fires */
41
+ then: Action<S, E>;
42
+ /** Optional event type filter - only check this rule for matching events */
43
+ eventType?: string;
44
+ }
45
+
46
+ /**
47
+ * A constraint that must hold true in valid states.
48
+ */
49
+ export interface Constraint<S extends PraxisState = PraxisState> {
50
+ /** Unique identifier for the constraint */
51
+ id: string;
52
+ /** Human-readable description of the constraint */
53
+ description?: string;
54
+ /** Function that returns true if the constraint is satisfied */
55
+ check: (state: S) => boolean;
56
+ /** Error message to return when constraint is violated */
57
+ errorMessage?: string;
58
+ }
59
+
60
+ /**
61
+ * Result of checking a constraint.
62
+ */
63
+ export interface ConstraintViolation {
64
+ constraintId: string;
65
+ message: string;
66
+ state?: PraxisState;
67
+ }
68
+
69
+ /**
70
+ * DSL builder for creating rules with a fluent interface.
71
+ */
72
+ export class RuleBuilder<
73
+ S extends PraxisState = PraxisState,
74
+ E extends PraxisEvent = PraxisEvent
75
+ > {
76
+ private rule: Partial<Rule<S, E>> = {};
77
+
78
+ /**
79
+ * Set the rule ID.
80
+ */
81
+ id(id: string): this {
82
+ this.rule.id = id;
83
+ return this;
84
+ }
85
+
86
+ /**
87
+ * Set the rule description.
88
+ */
89
+ describe(description: string): this {
90
+ this.rule.description = description;
91
+ return this;
92
+ }
93
+
94
+ /**
95
+ * Set the rule priority.
96
+ */
97
+ priority(priority: number): this {
98
+ this.rule.priority = priority;
99
+ return this;
100
+ }
101
+
102
+ /**
103
+ * Set the event type filter.
104
+ */
105
+ on(eventType: string): this {
106
+ this.rule.eventType = eventType;
107
+ return this;
108
+ }
109
+
110
+ /**
111
+ * Set the condition for when the rule should fire.
112
+ */
113
+ when(condition: Condition<S, E>): this {
114
+ this.rule.when = condition;
115
+ return this;
116
+ }
117
+
118
+ /**
119
+ * Set the action to execute when the rule fires.
120
+ */
121
+ then(action: Action<S, E>): this {
122
+ this.rule.then = action;
123
+ return this;
124
+ }
125
+
126
+ /**
127
+ * Build the final rule.
128
+ */
129
+ build(): Rule<S, E> {
130
+ if (!this.rule.id) {
131
+ throw new Error('Rule must have an id');
132
+ }
133
+ if (!this.rule.when) {
134
+ throw new Error('Rule must have a when condition');
135
+ }
136
+ if (!this.rule.then) {
137
+ throw new Error('Rule must have a then action');
138
+ }
139
+ return this.rule as Rule<S, E>;
140
+ }
141
+ }
142
+
143
+ /**
144
+ * DSL builder for creating constraints with a fluent interface.
145
+ */
146
+ export class ConstraintBuilder<S extends PraxisState = PraxisState> {
147
+ private constraint: Partial<Constraint<S>> = {};
148
+
149
+ /**
150
+ * Set the constraint ID.
151
+ */
152
+ id(id: string): this {
153
+ this.constraint.id = id;
154
+ return this;
155
+ }
156
+
157
+ /**
158
+ * Set the constraint description.
159
+ */
160
+ describe(description: string): this {
161
+ this.constraint.description = description;
162
+ return this;
163
+ }
164
+
165
+ /**
166
+ * Set the constraint check function.
167
+ */
168
+ check(check: (state: S) => boolean): this {
169
+ this.constraint.check = check;
170
+ return this;
171
+ }
172
+
173
+ /**
174
+ * Set the error message for constraint violations.
175
+ */
176
+ message(errorMessage: string): this {
177
+ this.constraint.errorMessage = errorMessage;
178
+ return this;
179
+ }
180
+
181
+ /**
182
+ * Build the final constraint.
183
+ */
184
+ build(): Constraint<S> {
185
+ if (!this.constraint.id) {
186
+ throw new Error('Constraint must have an id');
187
+ }
188
+ if (!this.constraint.check) {
189
+ throw new Error('Constraint must have a check function');
190
+ }
191
+ return this.constraint as Constraint<S>;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Factory functions for creating rules and constraints using the DSL.
197
+ */
198
+
199
+ /**
200
+ * Create a new rule using the fluent builder API.
201
+ */
202
+ export function rule<
203
+ S extends PraxisState = PraxisState,
204
+ E extends PraxisEvent = PraxisEvent
205
+ >(): RuleBuilder<S, E> {
206
+ return new RuleBuilder<S, E>();
207
+ }
208
+
209
+ /**
210
+ * Create a new constraint using the fluent builder API.
211
+ */
212
+ export function constraint<S extends PraxisState = PraxisState>(): ConstraintBuilder<S> {
213
+ return new ConstraintBuilder<S>();
214
+ }