@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.
- package/FRAMEWORK.md +420 -0
- package/LICENSE +21 -0
- package/README.md +1310 -0
- package/dist/adapters/cli.d.ts +43 -0
- package/dist/adapters/cli.d.ts.map +1 -0
- package/dist/adapters/cli.js +126 -0
- package/dist/adapters/cli.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +26 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +233 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/cloud.d.ts +27 -0
- package/dist/cli/commands/cloud.d.ts.map +1 -0
- package/dist/cli/commands/cloud.js +232 -0
- package/dist/cli/commands/cloud.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +25 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +168 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +179 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cloud/auth.d.ts +51 -0
- package/dist/cloud/auth.d.ts.map +1 -0
- package/dist/cloud/auth.js +194 -0
- package/dist/cloud/auth.js.map +1 -0
- package/dist/cloud/billing.d.ts +184 -0
- package/dist/cloud/billing.d.ts.map +1 -0
- package/dist/cloud/billing.js +179 -0
- package/dist/cloud/billing.js.map +1 -0
- package/dist/cloud/client.d.ts +39 -0
- package/dist/cloud/client.d.ts.map +1 -0
- package/dist/cloud/client.js +176 -0
- package/dist/cloud/client.js.map +1 -0
- package/dist/cloud/index.d.ts +44 -0
- package/dist/cloud/index.d.ts.map +1 -0
- package/dist/cloud/index.js +44 -0
- package/dist/cloud/index.js.map +1 -0
- package/dist/cloud/marketplace.d.ts +166 -0
- package/dist/cloud/marketplace.d.ts.map +1 -0
- package/dist/cloud/marketplace.js +159 -0
- package/dist/cloud/marketplace.js.map +1 -0
- package/dist/cloud/provisioning.d.ts +110 -0
- package/dist/cloud/provisioning.d.ts.map +1 -0
- package/dist/cloud/provisioning.js +148 -0
- package/dist/cloud/provisioning.js.map +1 -0
- package/dist/cloud/relay/endpoints.d.ts +62 -0
- package/dist/cloud/relay/endpoints.d.ts.map +1 -0
- package/dist/cloud/relay/endpoints.js +217 -0
- package/dist/cloud/relay/endpoints.js.map +1 -0
- package/dist/cloud/relay/health/index.d.ts +5 -0
- package/dist/cloud/relay/health/index.d.ts.map +1 -0
- package/dist/cloud/relay/health/index.js +9 -0
- package/dist/cloud/relay/health/index.js.map +1 -0
- package/dist/cloud/relay/stats/index.d.ts +5 -0
- package/dist/cloud/relay/stats/index.d.ts.map +1 -0
- package/dist/cloud/relay/stats/index.js +9 -0
- package/dist/cloud/relay/stats/index.js.map +1 -0
- package/dist/cloud/relay/sync/index.d.ts +5 -0
- package/dist/cloud/relay/sync/index.d.ts.map +1 -0
- package/dist/cloud/relay/sync/index.js +9 -0
- package/dist/cloud/relay/sync/index.js.map +1 -0
- package/dist/cloud/relay/usage/index.d.ts +5 -0
- package/dist/cloud/relay/usage/index.d.ts.map +1 -0
- package/dist/cloud/relay/usage/index.js +9 -0
- package/dist/cloud/relay/usage/index.js.map +1 -0
- package/dist/cloud/sponsors.d.ts +81 -0
- package/dist/cloud/sponsors.d.ts.map +1 -0
- package/dist/cloud/sponsors.js +130 -0
- package/dist/cloud/sponsors.js.map +1 -0
- package/dist/cloud/types.d.ts +169 -0
- package/dist/cloud/types.d.ts.map +1 -0
- package/dist/cloud/types.js +7 -0
- package/dist/cloud/types.js.map +1 -0
- package/dist/components/index.d.ts +43 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +17 -0
- package/dist/components/index.js.map +1 -0
- package/dist/core/actors.d.ts +95 -0
- package/dist/core/actors.d.ts.map +1 -0
- package/dist/core/actors.js +158 -0
- package/dist/core/actors.js.map +1 -0
- package/dist/core/component/generator.d.ts +122 -0
- package/dist/core/component/generator.d.ts.map +1 -0
- package/dist/core/component/generator.js +307 -0
- package/dist/core/component/generator.js.map +1 -0
- package/dist/core/engine.d.ts +92 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +199 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/introspection.d.ts +141 -0
- package/dist/core/introspection.d.ts.map +1 -0
- package/dist/core/introspection.js +208 -0
- package/dist/core/introspection.js.map +1 -0
- package/dist/core/logic/generator.d.ts +76 -0
- package/dist/core/logic/generator.d.ts.map +1 -0
- package/dist/core/logic/generator.js +339 -0
- package/dist/core/logic/generator.js.map +1 -0
- package/dist/core/pluresdb/generator.d.ts +58 -0
- package/dist/core/pluresdb/generator.d.ts.map +1 -0
- package/dist/core/pluresdb/generator.js +162 -0
- package/dist/core/pluresdb/generator.js.map +1 -0
- package/dist/core/protocol.d.ts +121 -0
- package/dist/core/protocol.d.ts.map +1 -0
- package/dist/core/protocol.js +46 -0
- package/dist/core/protocol.js.map +1 -0
- package/dist/core/rules.d.ts +120 -0
- package/dist/core/rules.d.ts.map +1 -0
- package/dist/core/rules.js +81 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/schema/loader.d.ts +47 -0
- package/dist/core/schema/loader.d.ts.map +1 -0
- package/dist/core/schema/loader.js +189 -0
- package/dist/core/schema/loader.js.map +1 -0
- package/dist/core/schema/normalize.d.ts +72 -0
- package/dist/core/schema/normalize.d.ts.map +1 -0
- package/dist/core/schema/normalize.js +190 -0
- package/dist/core/schema/normalize.js.map +1 -0
- package/dist/core/schema/types.d.ts +370 -0
- package/dist/core/schema/types.d.ts.map +1 -0
- package/dist/core/schema/types.js +161 -0
- package/dist/core/schema/types.js.map +1 -0
- package/dist/dsl/index.d.ts +152 -0
- package/dist/dsl/index.d.ts.map +1 -0
- package/dist/dsl/index.js +132 -0
- package/dist/dsl/index.js.map +1 -0
- package/dist/dsl.d.ts +124 -0
- package/dist/dsl.d.ts.map +1 -0
- package/dist/dsl.js +130 -0
- package/dist/dsl.js.map +1 -0
- package/dist/examples/advanced-todo/index.d.ts +55 -0
- package/dist/examples/advanced-todo/index.d.ts.map +1 -0
- package/dist/examples/advanced-todo/index.js +222 -0
- package/dist/examples/advanced-todo/index.js.map +1 -0
- package/dist/examples/auth-basic/index.d.ts +17 -0
- package/dist/examples/auth-basic/index.d.ts.map +1 -0
- package/dist/examples/auth-basic/index.js +122 -0
- package/dist/examples/auth-basic/index.js.map +1 -0
- package/dist/examples/cart/index.d.ts +19 -0
- package/dist/examples/cart/index.d.ts.map +1 -0
- package/dist/examples/cart/index.js +202 -0
- package/dist/examples/cart/index.js.map +1 -0
- package/dist/examples/hero-ecommerce/index.d.ts +39 -0
- package/dist/examples/hero-ecommerce/index.d.ts.map +1 -0
- package/dist/examples/hero-ecommerce/index.js +506 -0
- package/dist/examples/hero-ecommerce/index.js.map +1 -0
- package/dist/examples/svelte-counter/index.d.ts +31 -0
- package/dist/examples/svelte-counter/index.d.ts.map +1 -0
- package/dist/examples/svelte-counter/index.js +123 -0
- package/dist/examples/svelte-counter/index.js.map +1 -0
- package/dist/flows.d.ts +125 -0
- package/dist/flows.d.ts.map +1 -0
- package/dist/flows.js +160 -0
- package/dist/flows.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/pluresdb.d.ts +56 -0
- package/dist/integrations/pluresdb.d.ts.map +1 -0
- package/dist/integrations/pluresdb.js +46 -0
- package/dist/integrations/pluresdb.js.map +1 -0
- package/dist/integrations/svelte.d.ts +306 -0
- package/dist/integrations/svelte.d.ts.map +1 -0
- package/dist/integrations/svelte.js +447 -0
- package/dist/integrations/svelte.js.map +1 -0
- package/dist/registry.d.ts +94 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +181 -0
- package/dist/registry.js.map +1 -0
- package/dist/runtime/terminal-adapter.d.ts +105 -0
- package/dist/runtime/terminal-adapter.d.ts.map +1 -0
- package/dist/runtime/terminal-adapter.js +113 -0
- package/dist/runtime/terminal-adapter.js.map +1 -0
- package/dist/step.d.ts +34 -0
- package/dist/step.d.ts.map +1 -0
- package/dist/step.js +111 -0
- package/dist/step.js.map +1 -0
- package/dist/types.d.ts +63 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/docs/MONETIZATION.md +394 -0
- package/docs/TERMINAL_NODE.md +588 -0
- package/docs/guides/canvas.md +389 -0
- package/docs/guides/getting-started.md +347 -0
- package/docs/guides/history-state-pattern.md +618 -0
- package/docs/guides/orchestration.md +617 -0
- package/docs/guides/parallel-state-pattern.md +767 -0
- package/docs/guides/svelte-integration.md +691 -0
- package/package.json +96 -0
- package/src/__tests__/actors.test.ts +270 -0
- package/src/__tests__/billing.test.ts +175 -0
- package/src/__tests__/cloud.test.ts +247 -0
- package/src/__tests__/dsl.test.ts +154 -0
- package/src/__tests__/edge-cases.test.ts +475 -0
- package/src/__tests__/engine.test.ts +137 -0
- package/src/__tests__/generators.test.ts +270 -0
- package/src/__tests__/introspection.test.ts +321 -0
- package/src/__tests__/protocol.test.ts +40 -0
- package/src/__tests__/provisioning.test.ts +162 -0
- package/src/__tests__/schema.test.ts +241 -0
- package/src/__tests__/svelte-integration.test.ts +431 -0
- package/src/__tests__/terminal-node.test.ts +352 -0
- package/src/adapters/cli.ts +175 -0
- package/src/cli/commands/auth.ts +271 -0
- package/src/cli/commands/cloud.ts +281 -0
- package/src/cli/commands/generate.ts +225 -0
- package/src/cli/index.ts +190 -0
- package/src/cloud/README.md +383 -0
- package/src/cloud/auth.ts +245 -0
- package/src/cloud/billing.ts +336 -0
- package/src/cloud/client.ts +221 -0
- package/src/cloud/index.ts +121 -0
- package/src/cloud/marketplace.ts +303 -0
- package/src/cloud/provisioning.ts +254 -0
- package/src/cloud/relay/endpoints.ts +307 -0
- package/src/cloud/relay/health/function.json +17 -0
- package/src/cloud/relay/health/index.ts +10 -0
- package/src/cloud/relay/host.json +15 -0
- package/src/cloud/relay/local.settings.json +8 -0
- package/src/cloud/relay/stats/function.json +17 -0
- package/src/cloud/relay/stats/index.ts +10 -0
- package/src/cloud/relay/sync/function.json +17 -0
- package/src/cloud/relay/sync/index.ts +10 -0
- package/src/cloud/relay/usage/function.json +17 -0
- package/src/cloud/relay/usage/index.ts +10 -0
- package/src/cloud/sponsors.ts +213 -0
- package/src/cloud/types.ts +198 -0
- package/src/components/README.md +125 -0
- package/src/components/TerminalNode.svelte +457 -0
- package/src/components/index.ts +46 -0
- package/src/core/actors.ts +205 -0
- package/src/core/component/generator.ts +432 -0
- package/src/core/engine.ts +243 -0
- package/src/core/introspection.ts +329 -0
- package/src/core/logic/generator.ts +420 -0
- package/src/core/pluresdb/generator.ts +229 -0
- package/src/core/protocol.ts +132 -0
- package/src/core/rules.ts +167 -0
- package/src/core/schema/loader.ts +247 -0
- package/src/core/schema/normalize.ts +322 -0
- package/src/core/schema/types.ts +557 -0
- package/src/dsl/index.ts +218 -0
- package/src/dsl.ts +214 -0
- package/src/examples/advanced-todo/App.svelte +506 -0
- package/src/examples/advanced-todo/README.md +371 -0
- package/src/examples/advanced-todo/index.ts +309 -0
- package/src/examples/auth-basic/index.ts +163 -0
- package/src/examples/cart/index.ts +259 -0
- package/src/examples/hero-ecommerce/index.ts +657 -0
- package/src/examples/svelte-counter/index.ts +168 -0
- package/src/flows.ts +268 -0
- package/src/index.ts +154 -0
- package/src/integrations/pluresdb.ts +93 -0
- package/src/integrations/svelte.ts +617 -0
- package/src/registry.ts +223 -0
- package/src/runtime/terminal-adapter.ts +175 -0
- package/src/step.ts +151 -0
- package/src/types.ts +70 -0
- package/templates/basic-app/README.md +147 -0
- package/templates/fullstack-app/README.md +279 -0
package/src/dsl/index.ts
ADDED
|
@@ -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
|
+
}
|